2013/01/30

All the little houses falling over just right

To boot CentOS 5, all the following needs to be set up and running BEFORE you reboot your computer.

  1. GRUB installed on the boot drive (aka sda, ata1, sata1);
  2. a /boot that grub can find and read. In particular, it must be small and at the front of the boot drive;
  3. a kernel+initrd that contains the drivers to find and mount the final root filesystem (aka /); this can involve swearing, repeated applications of mkinitrd. At worse, you can pull initrd apart with gzip+cpio, add the drivers, modify init and repack it. Note that this is not a shell script but a nash script. CentOS 6 has moved to dash;
  4. a / that has a working init and all that entails (obviously);
  5. an /etc/fstab that won't fail out because fsck -A can't find a drive or partition. If you've moved / to another partition or to LVM, then you need to update fstab. You can set the last 2 columns to 0 if you don't want fsck to check that mount.

2013/01/29

Booting from a ghost partition

The most insane things can happen. Booting a new kernel, you see it in /boot/grub/grub.conf, but you don't see it in the GRUB menu at boot time. This has happened to me twice recently. First time, I lost 2 hours and thought I was going mad before I figured it out.

The cause is that I have /boot as a software RAID (/dev/md0, raid1 of sda1, sdb1, sdc1). While GRUB supports Linux software RAID1, it only does so partially; it assumes that the first drive of the array is the same as the others and uses it as an ext3 (or other) partition. What had happened a few weeks back was that sda1 had become the spare drive. This was caused by a mislabeled ICY DOCK; what I thought was sda became sdc, and sdc, sda. This first time I gave up before figuring it out, copied /boot into a temp directory, reformatted sda1 as a straight ext3 and copied /boot back into it.

This second time, I had already seen it and new where to look. That is /proc/mdstat. And to make sure that sda1 was an active part of the array. Sure enough, sda1 wasn't even in the array! So

mdadm /dev/md0 --add /dev/sda1
Then wait a minute for the RAID to rebuild, reboot and everything was fine.

This particular array had 3 active drives, no spares. I had replaced sda at some point and while I had added it to the main array (md1, raid5) I hadn't done so for md0. Why does md0 have no spares? It seemed like a good idea at the time. Repairing a computer that won't boot is annoying. Having all active, no spares means you never (in theory) end up with a disk that won't, should the first one give up the disk. And given that /boot is only written to once every blue moon, so it's not like I'm stressing the spare at all.

In the second case all 3 partitions of md0 are active; there are no spare drives. Had it been a case like first one, I would have forced sda1 to be an active partition in the array. Something like

mdadm /dev/md0 --fail /dev/sdc1 # cause the array to rebuild to sda1+sdb1
# now get sdc1 as the spare
mdadm /dev/md0 --remove /dev/sdc1 
mdadm /dev/md0 --add /dev/sdc1

2013/01/25

And now for something silly

I've mentioned Gunilla before. Here is a small Perl program, based on code by Yaakov.

Usage :

set-message
Sets the message on default printer to "Insert Coin"
set-message something
Sets the message on default printer to "something"
set-message 10.0.0.12 "Hello World"
Sets the message on printer at 10.0.0.12 to "Hello World"

I made my life harder (of course) by supporting \n. To do this, the program needs to know the width of your display ($WIDTH). Adding something like ~/bin/gunilla $(date +%A\\n%Y/%m/%d) to a crontab might be useful.

#!/usr/bin/perl

use strict;
use warnings;
use IO::Socket;

# Configuration:
my $WIDTH = 16;
my $peeraddr = 'gunilla';

my $rdymsg;
if( @ARGV == 0 ) {
    $rdymsg = 'Insert Coin';
}
elsif( @ARGV == 1 ) {
    $rdymsg = $ARGV[0];
}
elsif( @ARGV == 2 ) {
    ( $peeraddr, $rdymsg ) = @ARGV;
}
else {
    die "usage: $0 [] [\"\"]\n";
}
 
$rdymsg =~ s{^(.*)(\n|\\n)}{newline($1)}e;

my $socket = IO::Socket::INET->new(
        PeerAddr  => $peeraddr,
        PeerPort  => "9100",   
        Proto     => "tcp",    
        Type      => SOCK_STREAM
    ) or die "Could not create socket: $!";

my $data = <<EOJ;
\e%-12345X\@PJL JOB
\@PJL RDYMSG DISPLAY="$rdymsg"
\@PJL EOJ
\e%-12345X
EOJ
   
print $socket $data;

sub newline
{
    my( $text ) = @_;
    my $l = $WIDTH - length $text;
    $l = 1 if $l < 1;
    return $text. ( ' ' x $l );   
}