2014/06/12

End of an era

A hard drive was dying on Billy.

Thu Jun 12 19:15:31 EDT 2014
19:15:31 up 1316 days, 19:15,  2 users,  load average: 0.97, 0.97, 0.77

But the server is rented. So while I would have tried to do a hot swap, iWeb wisely wanted to do a shutdown.

Thu Jun 12 20:36:03 EDT 2014
20:36:03 up 8 min, 12 users,  load average: 2.79, 1.28, 0.56

Oh well.

2014/04/30

Furthermore.

A corollary to my previous dictum that a method may make a decisions OR do something is that you want to cut a larger into smaller pieces. And each piece generally looks like the following:

sub doing_something {
    my( $self ) = @_;
    $self->prepare_something;
    if( $self->is_it_time_to_do_something ) {
        $self->before_something;
        $self->something;
        $self->after_something;
   }
   $self->unprepare_something;
}

In the above, something is just the name of the particular small piece of the larger task. The prepare_something/unprepare_something calls are there to avoid all possible side-effects in is_it_time_to_do_something. I would use before_something/after_something are there for things logging, timing, transactions and other "admin" actions that aren't related to something.

I feel like I've been infected by all the Java I did last spring.

2014/04/09

Parsing HTTP::Request->content with CGI.pm

Back in the mists of time, when the Web was young and unconquered, Lincoln Stein wrote a module for Perl that would allow people to easily deal with parameters handed to a CGI program and to generate HTML. This module eventually grew to include not one but several kitchen sinks. It includes its own autoload mechanism, it's own file handle class and more. It Just Works when called FastCGI, Perlex, mod_perl and others.

While CGIs have all but disappeared, this module is still very useful for handling all the finicky edge cases for dealing with HTTP request content. But if you write your own web server environment, using CGI.pm to parse the HTTP content can get be hard. You basically have to fake it out.

This is how you get the params from a GET request.

# $req is a HTTP::Request object
local $ENV{REQUEST_METHOD} = 'GET';
local $CGI::PERLEX = $CGI::PERLEX = "CGI-PerlEx/Fake";
local $ENV{CONTENT_TYPE} = $req->header( 'content-type' );
local $ENV{'QUERY_STRING'} = $req->uri->query;
my $cgi = CGI->new();

# Now use $cgi as you wish

And here we parse the params from a POST request. Note that POST request can be big. Very big. If you aren't careful, they will fill up your memory. Always check Content-Length before reading in a POST request. In the following code, all the content was written to a file.

# $req is a HTTP::Request object
# $file is a filename that contains the unparsed request content
local $ENV{REQUEST_METHOD} = 'POST';
local $CGI::PERLEX = $CGI::PERLEX = "CGI-PerlEx/Fake";
local $ENV{CONTENT_TYPE} = $req->header( 'content-type' );
local $ENV{CONTENT_LENGTH} = $req->header( 'content-length' );
local $CGITempFile::TMPDIRECTORY = "/YOUR/TEMP/DIR/HERE";
# CGI->read_from_client reads from STDIN
my $keep = IO::File->new( "<&STDIN" ) or die "Unable to reopen STDIN: $!";
open STDIN, "<$file" or die "Reopening STDIN failed: $!";
my $cgi = CGI->new();
open STDIN, "<&".$keep->fileno or die "Unable to reopen $keep: $!";
undef $keep;
unlink $file

# Now use $cgi as you wish

The fun is that CGI will only read POST data from STDIN, so we have to redirect that to our file, saving and restoring the previous STDIN.

The above code also works when you are uploading a file with multipart/form-data which is how I got caught up in all this kerfuffle.

It's really to bad that one can't just do

my $cgi = CGI->new( $req );

2014/03/27

Dave Brubeck Quartet

And now for something completely different : Dave Brubeck Quartet's Time Out album is currently on heavy play. I know I'm late to the party. In fact, I was born to late for this party. But I really really digging the combination of mellow, swing and exotica on this album.

2014/03/20

25,000 Linux/UNIX Servers Infected with Malware

And this is a big reason is why you pay a real sysadmin to do your system administration.

In short, people were installing WordPress badly (friends don't let friends use PHP). They were allowing password authenticated ssh login over the internet. They were doing chmod 0777 ~apache/html_docs. They were doing other highly unsafe things.

If you can't see the problem with these things, then you need to talk to a professional sysadmin.

2014/03/19

Remove an LVM volume group from the kernel

I FINALLY found it!

The problem: You are messing around with loopback files and volume groups. You've removed the loopback, but the VG stays in the kernel's internal list. vgremove is of course NOT how you get rid of it.

The sane way of doing this is:

vgchange -a n VolGroup00
kpartx -d /dev/nbd0
qemu-nbd -d /dev/nbd0
vgscan

But maybe you killed qemu-nbd by mistake? Or maybe your partition is long gone. Then you need to use dmsetup to remove all traces:

# ls -l /dev/mapper/
total 0
lrwxrwxrwx. 1 root root      7 Mar 18 22:08 GEORGE2-root -> ../dm-0
lrwxrwxrwx. 1 root root      7 Mar 18 22:08 GEORGE2-swap -> ../dm-1
lrwxrwxrwx. 1 root root      7 Mar 19 15:44 Test02-LogVol00 -> ../dm-4
lrwxrwxrwx. 1 root root      7 Mar 19 15:44 Test02-LogVol01 -> ../dm-5
crw-rw----. 1 root root 10, 58 Mar 18 22:08 control
# dmsetup info /dev/dm-4
Name:              Test02-LogVol00
State:             ACTIVE
Read Ahead:        256
Tables present:    LIVE
Open count:        0
Event number:      0
Major, minor:      253, 4
Number of targets: 1
UUID: LVM-zo1BvaMXr6TS1knhxoyjhtItHEaIVH4wGJz2s2w8w24za3486Aa9ur0igGMxpLf7
# dmsetup remove /dev/dm-4
# dmsetup remove /dev/dm-5
# vgscan
  Reading all physical volumes.  This may take a while...
  Found volume group "GEORGE2" using metadata type lvm2

And there was much rejoicing.

2014/03/18

I am INVINCIBLE!

Nothing is beyond me. When it comes to computers, I am all conquering.

That actually might be an exaggeration. But I just pulled off a stunt that really impressed me.

I'm moving all my systems from CentOS 5 to CentOS 6. (Why so soon? Shut up) In the process, I need to move my VMs from VMware Server 1 (Seriously? Shut up) to KVM (libvirt specificly). For the most part, I'm actually starting up whole new VMs and reconfiging them. But I still might want to look at my old data, fetch old files, and what not. This means being able to read VMware's vmdk files. This is "easy":

modprobe nbd max_part=8
qemu-nbd -r --connect=/dev/nbd0 /vmware/files/sda.vmdk
kpartx -a /dev/nbd0
vgscan
vgchange -a y VolGroup00
mount -o ro /dev/mapper/VolGroup00-LogVol00 /mnt/files

There are 3 complications to this:

First, you can't have multiple VGs with the same name active at once. Work around is to only mount one at a time. You can renamed VGs with vgrename but that's a job for another day.

Next off, I chose to have my vmdk split into multiple 2GB files. This makes copying them around so much more fun. But qemu only understands monolithic files, so you need vmware-vdiskmanager to convert them. Specificly

vmware-vdiskmanager -r /vmware/files/sda.vmdk -t 0 /vmware/files/single.vmdk

Lastly (and this is the main point of this post) CENTOS 6 DOESN'T SHIP WITH NBD! After WTFing about as hard as I could, I googled around for one. Someone must have needed nbd at some point, surely. The only solution I found was to recompile the kernel from scratch. Which is stupid. As a work around, I used the kernel-lt from elrepo. But the real solution would be a kmod. I thought doing a kmod would be hard, so I set aside a few hours. Turns out, it's really easy and I got it right on the first try.

tl;dr - rpm -ivh kmod-nbd-0.0-1.el6.x86_64.rpm

I based my kmod on kmod-jfs from elrepo.

  1. Install the kmod-jfs SRPM;
  2. Copy jfs-kmod.spec to nbd-kmod.spec;
  3. Copy kmodtool-jfs-el6.sh to kmodtool-nbd-el6.sh
  4. Edit nbd-kmod.spec. You have to change kmod_name and the %changelog section. You might also want to change kversion to your current kernel (uname -r). If not, you need to add --define "kversion $(uname -r)" when running rpmbuild;
  5. Create nbd-0.0.tar.bz2;
  6. Build, install and test the new module.
    rpmbuild -ba nbd-kmod.spec
    rpm -ivh ~/rpmbuild/RPMS/x86_64/kmod-nbd-0.0-1.el6.x86_64.rpm
    modprobe nbd
    ls -l /dev/nbd*
  7. FLAWLESS VICTORY!

The hard part (of course) is that I wasn't sure what to put in nbd-0.0.tar.bz2. The contents of jfs-0.0.tar.bz2 just look like the files from drivers/jfs in the kernel tree with Kconfig and Makefile added on. So I pull down the kernel SRPM, did a rpmbuild -bp on that (just commend out all the BuildRequires that give you grief. You aren't doing a full build.) Then I poked around for nbd in ~/rpmbuild/BUILD/vanilla-2.6.32-431.5.1.el6/. Turns out there's only nbd.c and nbd.h. So that goes in the pot. I copied over the Makefile from jfs, modifying it slightly because jfs is spread over multiple source files. Kconfig looked like kernel configuration vars. I just copied BLK_DEV_NBD out of vanilla-2.6.32-431.5.1.el6/drivers/block/Kconfig.

This entire process took roughly 1 hours. It worked on the first try. Of course, all the magic is in kmodtool-nbd-el6.sh. But I was expecting a lot of pain. Instead it worked on the first try. I was so surprised I did modprobe -r nbd ; ls -l /dev/nbd* just to make sure I wasn't getting a false positive.