Make your workstation speak

You can make your linux workstation speak using espeak.  Espeak is a text-to-speech synthesizer.  If you have Ubuntu desktop, then you should already find it installed.

If you are a Ubuntu user and you don’t find espeak already installed, then you need to install the  following:

$ sudo apt-get install espeak espeak-data

If you have Fedora, you can install espeak, using yum.

$ sudo yum install espeak

To use espeak, go to the cmdline.

espeak “hello, this is a test

or

espeak -f <textfile>

or

cat <textfile> | espeak

You can control the speed at which words are spoken.

(To slow it down…default speed is 160)

espeak -s 120 “hello, this is a test”

To save the spoken words as a .wav file.  Rather than direct output.

espeak -w hello.wav “hello, this is a test”

You can select different voices.  To get a list of available voices use –voices.

Lets do the same but with a bit of italian accent.

espeak -v it “hello, this is a test”

Espeak also supports, though not fully, Speech Synthesizer Markup Language (SSML).  This is a markup language similar to xml which controls the way text is pronounced.

To see the details of how espeak pronounces words, use the -x and -X options.  This will output the phonetic codes associated with the spoken words, and whether the word is looked up or translated.

I hope this was useful.  Leave a comment if you find a particularly interesting way to use espeak.

Mounting raw image files and kpartx

I’m going to demonstrate a tool for working with raw image files of hard disks.  That tool is kpartx.  It is used to read block devices and to create device mappings of partitions.  IOW, each device file in /dev/mapper will represent a disk partition or a disk volume.

Let me show you an example and you will see what I mean.

Sometimes I use the dd command to backup my hard drives.  It’s easy to do.  You boot from a live CD and you use the dd command and netcat to backup your drive over your LAN to your backup server.   I covered that in a previous post, so I won’t go into great detail again.  I’ll skip ahead.


$ sudo dd if=/dev/sda | nc 192.168.140.11 12345

Now with my raw image file (gothbook.img) on my backup server, I want to examine that image file.  I can use kpartx to list any partitions that are to be found on that drive image.

# kpartx -l gothbook.img
loop1p1 : 0 512000 /dev/loop1 63
loop1p2 : 0 512000 /dev/loop1 512063
loop1p3 : 0 45056000 /dev/loop1 1024063
loop1p5 : 0 8388608 /dev/loop1 46090548
loop1p6 : 0 39070017 /dev/loop1 54492543
loop1p7 : 0 62733762 /dev/loop1 93562623

I can see from the output of kpartx that my drive image contains 6 partitions.  I can see their starting offsets.  The first column tells me the names of the device files that will be created if I choose to add these device partitions.  Lets add them now.

# kpartx -a -v gothbook.img
add map loop1p1 (253:6): 0 512000 linear /dev/loop1 63
add map loop1p2 (253:7): 0 512000 linear /dev/loop1 512063
add map loop1p3 (253:8): 0 45056000 linear /dev/loop1 1024063
add map loop1p5 (253:9): 0 8388608 linear /dev/loop1 46090548
add map loop1p6 (253:10): 0 39070017 linear /dev/loop1 54492543
add map loop1p7 (253:11): 0 62733762 linear /dev/loop1 93562623

# ls -l /dev/mapper
total 0
crw-rw---- 1 root root  10, 62 2010-06-15 17:40 control
brw-rw-r-- 1 neil neil 253,  6 2010-08-16 00:28 loop1p1
brw-rw-r-- 1 neil neil 253,  7 2010-08-16 00:28 loop1p2
brw-rw-r-- 1 neil neil 253,  8 2010-08-16 00:28 loop1p3
brw-rw-r-- 1 neil neil 253,  9 2010-08-16 00:28 loop1p5
brw-rw-r-- 1 neil neil 253, 10 2010-08-16 00:28 loop1p6
brw-rw-r-- 1 neil neil 253, 11 2010-08-16 00:28 loop1p7
The preceeding command added six device map files to /dev/mapper.  Each of these device files corresponds to a partition from that hard drive image.  We can now use these device files to mount these partitions and access any files they contain.
I want to mount the fifth partition (/dev/mapper/loop1p6) and have a look at its files.
# mkdir /mnt/sysimage

# mount /dev/mapper/loop1p6 /mnt/sysimage
# ls /mnt/sysimage
bin    dev   initrd.img      lost+found  opt   sbin     sys  var
boot   etc   initrd.img.old  media       proc  selinux  tmp  vmlinuz
cdrom  home  lib             mnt         root  srv      usr  vmlinuz.old
After mounting the device file, you can access the files contained on that partition.  When you are done, don’t forget to umount the partition and disconnect the device map files using kpartx.
# umount /mnt/sysimage
# kpartx -d -v gothbook.img

And that’s it.

Configuring grub to boot a fallback kernel

I’m writing this because the fallback behavior of grub seems broken or at the least poorly documented.

Here is the scenario.  I have a server in a remote location.  I have no access to the local console.  I need to update the kernel however there is a chance that this new kernel could crash and leave my server completely disabled.  In my scenario that is not an acceptable risk.

Fortunately grub can be manipulated enough so that a kernel is booted once and it if crashes, the system will reset and boot a previously known good kernel.

BTW, this is a RHEL5 system.  Here is my grub.conf file.

default=0
timeout=10
title RHEL known *good* kernel
root (hd0,0)
kernel /vmlinuz-2.6.18-164.el5 ro root=/dev/OS/vol1  rhgb quiet
initrd /initrd-2.6.18-164.el5.img
savedefault

title RHEL *new* kernel
root (hd0,0)
kernel /vmlinuz-2.6.18-194.8.1.el5 ro root=/dev/OS/vol1 rhgb quiet panic=5
initrd /initrd-2.6.18-194.8.1.el5.img

My “untested” kernel is the second item listed.  The kernel param panic=5 will cause the system to automatically reset after 5 seconds following the event of a kernel panic.  Otherwise, the server would be stuck in a crashed state until someone could physically intervene and power cycle the server.

Now comes the interesting part.  We need to tell grub to set the “untested” kernel as the default and to boot it only once.  We can do this from the grub shell.

# grub

grub> savedefault --default=1 --once
savedefault --default=1 --once
grub> quit

As you can see I tell grub to save the “untested” kernel (–default=1) as the default for one-time only (–once).  This makes the second menu item be the default item to be booted next.  After that, the default item will go back to being the one designated in the grub.conf file (default 0).

Now I can reboot the server with extra confidence.  If my kernel panics and crashes the server will soon reboot to the previous “good” kernel.  If the kernel does not panic and eventually proves itself to my satisfaction, then I can log back in and edit my grub.conf file such that my new kernel is now my known “good” kernel.

The official grub documentation gives a more elaborate example, which doesn’t work–At least not under RHEL5 or Centos5 distributions.   After googling around, it seems that others have the same issue with the documented features not working as expected.   If you have been struggling to get this to work, I hope you will enjoy my example.  It is easy to follow and hopefully it will give you the functionality that you need.

Using sqlite3

An sqlite database is an entire database that is contained in a single cross platform file.  It is used by many applications to store data and state information.  I’ll show you a few things to get you started using sqlite databases.

The Fedora software update tool,  yum, uses sqlite databases to store package information locally  from the yum repository.  I’ll be using my Fedora 12 server for this example.

First I’ll get some fresh data.


yum clean all
yum check-update

The fresh data will be found in /var/cache/yum.   I’m interested in files named *.sqlite.


find /var/cache/yum -name "*.sqlite"

/var/cache/yum/updates/1793ed5dc6773763df84b55d5515f96add374ca115e0e4151a70378f6ed323c8-primary.sqlite

The long filename is a bit unwieldy so I’ll copy it to a file that is easier to handle.


cp /var/cache/yum/updates/1793ed5dc6773763df84b55d5515f96add374ca115e0e4151a70378f6ed323c8-primary.sqlite /tmp/primary.sqlite

Now lets see a list of database tables.


sqlite3 /tmp/primary.sqlite .tables

conflicts  db_info    files      obsoletes  packages   provides   requires

Now lets dump the database to another file.


sqlite3 /tmp/primary.sqlite .dump > /tmp/primary.dump

Lets examine the dumpfile.  I want to see the column names from the packages table.


egrep "CREATE TABLE packages" /tmp/primary.dump
CREATE TABLE packages (  pkgKey INTEGER PRIMARY KEY,  pkgId TEXT,  name TEXT,  arch TEXT,  version TEXT,  epoch TEXT,  release TEXT,  summary TEXT,  description TEXT,  url TEXT,  time_file INTEGER,  time_build INTEGER,  rpm_license TEXT,  rpm_vendor TEXT,  rpm_group TEXT,  rpm_buildhost TEXT,  rpm_sourcerpm TEXT,  rpm_header_start INTEGER,  rpm_header_end INTEGER,  rpm_packager TEXT,  size_package INTEGER,  size_installed INTEGER,  size_archive INTEGER,  location_href TEXT,  location_base TEXT,  checksum_type TEXT);

Lets extract some data.


sqlite3 /tmp/primary.sqlite "select name, time_build, location_href from packages" | less

logstalgia|1277021205|logstalgia-1.0.0-1.fc12.x86_64.rpm
php-pear-Services-Weather|1257462896|php-pear-Services-Weather-1.4.5-1.fc12.noarch.rpm
geany-plugins-geanydoc|1266257186|geany-plugins-geanydoc-0.18-2.fc12.x86_64.rpm
dgc|1259416815|dgc-0.98-3.fc12.i686.rpm
autocorr-sk|1278184863|autocorr-sk-3.1.1-19.34.fc12.noarch.rpm
wireshark-devel|1268753377|wireshark-devel-1.2.6-2.fc12.i686.rpm
sazanami-mincho-fonts|1277357251|sazanami-mincho-fonts-0.20040629-9.1.fc12.noarch.rpm
moodle-cs|1277226792|moodle-cs-1.9.9-1.fc12.noarch.rpm
libmapi|1276209360|libmapi-6.40.0-2.fc12.i686.rpm
moodle-ro|1277226792|moodle-ro-1.9.9-1.fc12.noarch.rpm

As you can see sqlite is using the ‘|’ as a column separator.

Now lets do the same thing but with a perl program.  You’ll need the following two modules DBI and DBD::SQLite.


yum install perl-DBI perl-DBD-SQLite
#!/usr/bin/perl

use DBI;

$file="/tmp/primary.sqlite";

my $db = DBI->connect("dbi:SQLite:dbname=$file", { PrintError => 0,
RaiseError => 0 });

my $sth = $db->prepare("select name, time_build, location_href from packages");
$sth->execute();

my @row;
while ( @row = $sth->fetchrow_array() ) {
my ( $name, $time_build, $location_href ) = @row;
print "$name $time_build $location_href\n";
}
warn "Data fetching terminated early by error: $DBI::errstr\n"
if $DBI::err;

$db->disconnect
or warn "Error disconnecting: $DBI::errstr\n";

exit;

I hope you found this useful and it will inspire you to do more.