2013/02/19

PAR Wins!

New client, they set up a SuSE Enterprise Linux VM for me to install on. I approach this with trepidation. SLES is distinct from OpenSUSE, so I can't set up a VM at home for staging. Turns out my fears were misplaced; a PAR built on CentOS 5.8 runs on SLES 11.2 with only a minor problem. Colour me Impressed.

The minor problem was that some libraries aren't the same version. But a simple -l db -l gnutls -l gnutls-extra -l gnutls-openssl works around this. Of course, PAR::Packer only looks in LD_LIBRARY_PATH for libraries. MUST NOT BE SIDETRACKED INTO FIXING THIS.

Here's how to check shared libraries. First you build your PAR. Then extract it:

mkdir t
cd t
unzip ../your.par

Now scan that with the following code:

#!/usr/bin/perl

use strict;
use warnings;

use File::Finder;

my %already;
foreach my $lib ( File::Finder->name("*.so")->in('lib') ) {
    foreach my $line ( qx(ldd $lib) ) {
        chomp( $line );
        next unless $line =~ /(.+)\s+=>\s+(.+)\s\(0x/;
        my( $so, $file ) = ( $1, $2 );
        next if $file =~ m(^/lib);  # assume anything in /lib and /lib64 are OK
        next if $already{$file}++;
        print "rpm -qf $file # $lib\n";
    }
}

This will produce some commands that you then paste into a command prompt on the remote computer. If one of the lines doesn't show up, you will have to include it in the PAR. Note that sometimes one distro will put the library in /usr/lib64 and another distro will put it in /lib64 which might cause false negatives. I wonder what the overhead of including EVERY library in the PAR would be. MUST NOT BE SIDETRACKED.

2013/02/12

Installing fonts in CentOS

This is here because I keep forgetting:
fc-cache /path/to/fonts
. Also, under CentOS 6:
yum install dejavu-lgc-sans-mono-fonts \
 dejavu-sans-mono-fonts \
 gnu-free-mono-fonts \
 google-droid-sans-mono-fonts \
 liberation-mono-fonts \
 oflb-smonohand-fonts

2013/02/07

cups-lpd and syslog

cups-lpd logs everything to syslog. This fills up /var/log/messages unnecessarily. I finally pulled out the source code and went about writing a patch so that it logs to a file. And then someone on #cups indirectly pointed out that cups-lpd uses LOG_LPR faculty and syslog can filter with these. The trick is these 2 lines in /etc/syslog.conf
*.info;lpr.!info;mail.none;news.none;authpriv.none;cron.none        /var/log/messages
lpr.*   /var/log/cups/cups-lpd_log
The first one sends *.info, EXCEPT lpr.info to /var/log/messages. The second one sends lpr.* to /var/log/cups/cups-lpd_log. Note that this means that any other program that uses LOG_LPR will also log to the same file.

But that's not all; xinetd also logs to syslog. But it can be easily convinced to log to a file, with the following /etc/xinetd.d/cups-lpd:

service printer
{
    disable = no
    socket_type = stream
    protocol = tcp
    wait = no
    user = lp
    server = /usr/lib/cups/daemon/cups-lpd
    server_args = -o job-sheets=none
    log_type = FILE /var/log/cups/cups-lpd_log
}
So there you have: how to prevent cups-lpd from logging to syslog.

2013/02/03

Cogceco, you impress me

I got some postal mail from Cogceo. I thew it on the passenger seat and thought to myself "bet they are offering me a new, extra fast extra expensive bandwidth package." Because the cost of ISPs only ever goes up, unlike the cost of computers or the cost of renting a server, etc. Because there is zero competition amongst ISPs; for broadband I can have DSL or DOCSIS, provided by Bell or Cogeco. Both have resellers, but the basic rate is fixed by Bell or Cogeco. But I was wrong. The letter was to announce improved service (upload speed doubling) but not a rate increase. Yet.

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 );   
}