Friday, November 8, 2024

X-Commit-Powered-By "Header" - Updated!

 It's been a while since I've written a post here but I stopped by to pick up my script to add an X-Commit-Powered-By header to my git commit messages using a commit-msg hook and realized it's hopelessly broken now. If you want to read what it does, you should read the original post. I decided, it was time to update it for 2020s! This post covers the most interesting thing I learned while doing that.

The original script was:

#!/bin/sh
# Adds the currently playing iTunes track to the commit message

# Add a blank line
echo >> $1
state=`osascript -e 'tell application "iTunes" to player state as string'`;
if [ $state = "playing" ]; then
    artist=`osascript -e 'tell application "iTunes" to artist of current track as string'`;
    track=`osascript -e 'tell application "iTunes" to name of current track as string'`;
    echo "X-Commit-Powered-By: $artist - $track" >> $1;
else
    echo "X-Commit-Powered-By: Silent Meditation" >> $1;
fi

It clearly needed to be updated at the very least because the iTunes.app had long since been replaced by Music.app. A quick test of the osascript commands told me that replacing iTunes with Music worked just fine. Since I was going to update the script anyway, I wanted to improve it a bit further for myself.

I decided I wanted to keep a copy of the script in my ~/bin directory at all times for easier linking as a git hook. To that end, I needed a version of the script that would output the X-Commit-Powered-By header to standard output when I ran it from the ~/bin directory but to the commit message when it's run as a git commit-msg hook.

As you can read from the script source, however, it expects a filename as the first argument, $1, and appends the message to that file. Here you can find more details about that and other client-side git hooks. I could wrap all echo statements with an if statement that checks the value of $1 and branch to a version of that echo statement that outputs to the right location.

However, the ideal scenario would be to redirect standard output to a file if one was specified. After all we can do this when calling the script manually by just appending >> /path/to/filename after the script name. Why can't we do this from within the script?

I found myself on this Stack Overflow response to a similar question. Essentially all I had to do is close the file handle for standard output (file handle 1) and re-open that same file handle so it appends all its output to a file - $1 in my case. The bonus is that I don't have to append all of my echo lines with >> $1 any more - that just happens automatically.

With all that, the updated version of the script is:

#!/bin/sh
# Adds the currently playing Music track to the commit message

function displayHelp() {
    echo enable this as a git hook using:
    echo
    echo ln -s $0 /path/to/.git/hooks/commit-msg
}

if [ "$1" == "-h" ] || [ "$1" == "--help" ]; then
    displayHelp
    exit 1
fi

if [ "$1" != "" ]; then
    # We were given a file - presumably from git
    # Let's ensure all echos go to this file
    
    # https://stackoverflow.com/a/20564208/3766784
    # Close standard error file descriptor
    exec 2<&-

    # Open standard output to append to $1 file for write
    exec 1>>$1
fi

# Add a blank line
echo

state=`osascript -e 'tell application "Music" to player state as string'`;
if [ $state = "playing" ]; then
    artist=`osascript -e 'tell application "Music" to artist of current track as string'`;
    track=`osascript -e 'tell application "Music" to name of current track as string'`;
    echo "X-Commit-Powered-By: $artist - $track"
else
    echo "X-Commit-Powered-By: Silent Meditation"
fi

Put this script anywhere in your path, chmod +x it so it can be executed and directly linked to your .git/hooks/commit-msg and you can then run it without any arguments to test it out on standard output. Call it with a filename and it'll append its output to that file. Call it with the -h or --help argument to see how to link it to your .git/hooks directory.

One thing to keep in mind running in 2024 is that you should run the script at least once from the Terminal before installing it as a commit hook. It'll ask you for permission to let Terminal access your Music app which I figure will be necessary for it to run as a git commit hook - at least if you run git from the Terminal like I do.

Tuesday, July 28, 2015

X-Commit-Powered-By "Header"

Just wrote this commit-msg hook for git that just has me looking forward to each commit to the project.  It's only for OSX and its sole purpose is to add a tag that looks like a custom HTTP header which shows what song was playing when the user created the commit.  It only works with iTunes and can handle when iTunes is paused.

Here's an example of how it looks:
X-Commit-Powered-By: Cake - Short Skirt / Long Jacket
 When it detects that your iTunes is paused, it outputs this instead:
X-Commit-Powered-By: Silent Meditation
 You can change all this, of course, with a little bash-fu.

Without further ado, here's the script.  Move it to your git repository's .git/hooks directory and rename it to "commit-msg".  Throw in a chmod +x .git/hook/commit-msg and you're good to go!  If you already have a commit-msg hook, you can add this snippet to the end of it (minus the hash-bang line)

#!/bin/sh
# Adds the currently playing iTunes track to the commit message

# Add a blank line
echo >> $1

state=`osascript -e 'tell application "iTunes" to player state as string'`;
if [ $state = "playing" ]; then
    artist=`osascript -e 'tell application "iTunes" to artist of current track as string'`;
    track=`osascript -e 'tell application "iTunes" to name of current track as string'`;
    echo "X-Commit-Powered-By: $artist - $track" >> $1;
else
    echo "X-Commit-Powered-By: Silent Meditation" >> $1;
fi

 Happy committing!

Monday, October 1, 2012

Testing Validations using RSpec

I just ran into an interesting issue while testing a Rails application with RSpec.  A spec with the following line in it was failing:

    ar2.should_not be_valid
Here ar2 is a model that was constructed in a way that violated a logical constraint.  I attempted to make this spec pass by adding a custom validation method to the model as such:
class Registration < ActiveRecord::Base
  validate :fields_match

  private

  def fields_match
    return true if model2.nil?
    model2.model1.id == model1.id
  end
end
All field names have had their names changed to protect their identity.

This didn't work.  As it turns out merely returning false from the validation method isn't enough to mark a model as invalid.  This answer on StackOverflow helped identify the problem as not adding an error to the list of validation errors for the model.

An updated version where I add an error finally made everything pass:

class Registration < ActiveRecord::Base
  validate :fields_match

  private

  def fields_match
    return true if model2.nil?
    return true if model2.model1.id == model1.id
    errors.add(:model2_id, "model2 doesn't match model1")
  end
end

Wednesday, May 23, 2012

Setting up LVM on LUKS

I recently worked with Raju Chauhan to setup encrypted storage for a database and related files.  He brought up an interesting requirement to see if the encrypted storage could grow with the data as needed.  I hadn't dealt with that specific requirement in the past so I figured I'd see what my options were.  Thanks for that requirement Raju; I don't think I would've thought of doing this were it not for that :-)

After preliminary performance testing in which LUKS barely edged out TrueCrypt, I chose LUKS for the setup since it's integrated into the Linux kernel and seemed to be a better choice for larger filesystems.  For those who don't know, LUKS is a disk-encryption specification that is implemented using cryptsetup and the dm_crypt module in modern Linux kernels.

My solution: LVM on a bunch of LUKS devices to get the encryption and dynamic growth working together.  Here is how to play with that on your own machines if you have about 5G of space to work with and want to see how it looks.

Pre-requisite Packages

I did this on an Ubuntu system so the following pre-requisite package installation instructions are for that. You'll need to ensure the appropriate packages for your distribution are installed before proceeding.

aptitude install cryptsetup-luks lvm2

Creating LVM over LUKS Setup

Create 4 1G files corresponding to physical volumes:
  1. for i in 0 1 2 3; do dd if=/dev/zero of=/pv0$i.luks bs=1M count=0 seek=1000; done
  2. ls -l /pv*.luks
Attach all of them to loopback devices:
  1. for i in 0 1 2 3; do losetup /dev/loop$i /pv0$i.luks; done
  2. losetup -a
Setup all devices as LUKS volumes (answer all prompts):
  1. for i in 0 1 2 3; do cryptsetup luksFormat /dev/loop$i; done
Open all LUKS devices:
  1. for i in 0 1 2 3; do cryptsetup luksOpen /dev/loop$i pv0$i.luks.device; done
Create LVM Physical Volumes from each LUKS device:
  1. for i in 0 1 2 3; do pvcreate /dev/mapper/pv0$i.luks.device; done
  2. pvdisplay
Create LVM Volume Group from all LUKS PVs:
  1. vgcreate vg0 `for i in 0 1 2 3; do echo /dev/mapper/pv0$i.luks.device; done`
  2. vgdisplay
Carve out a LVM Logical Volume from the vg0 Volume Group:
  1. lvcreate --size 3000M --name demolv vg0
  2. lvdisplay
At this time you have a LVM volume group named demolv that is sitting on top of two encrypted physical volumes that is each part of a single LUKS volume.  You can give each LUKS volume different passwords to increase security or you can give them all the same password to increase convenience.

Format and Mount Logical Volume

Format and mount the demolv Logical Volume with whatever filesystem you choose:
  1. mkfs.ext4 /dev/vg0/demolv
  2. mkdir /demo
  3. mount /dev/vg0/demolv /demo
At the end of all this, demolv will contain a filesystem that can be expanded by adding more LUKS volumes to the mix.  Feel free to create files and/or use this volume in any way you can think of with the knowledge that all the data you're storing is encrypted on disk.  Yes, this is quite cool!

Unmount and Detach

Once you're done playing with it (or when you're ready to shut down your system) you can run the following commands to unmount and detach everything.  These steps assume you followed the steps in this tutorial to the letter without changing any names.  If you changed names, you should change the corresponding names in the commands below:
  1. for lv in /dev/vg0/*; do lvchange -an $lv; done
  2. vgchange -an /dev/vg0
  3. for i in 0 1 2 3; do cryptsetup luksClose pv0$i.luks.device; done
  4. for i in 0 1 2 3; do losetup -d /dev/loop$i; done

Saturday, October 15, 2011

Smoother MKV playback with VLC

If you're seeing stuttering in any MKV videos you've ripped, try out wipe0wt's suggestion and turn off loop filters in VLC altogether.  It results in a dramatic improvement in playback quality.  The gist is:
  1. Open up VLC and choose Tools -> Preferences from the menu
  2. In the bottom left corner of the preferences window you'll see a "Show settings" area.  Make sure you change it from "Simple" to "All".  This will change the left side of the preferences window so it's a tree view instead of a collection of icons.
  3. Navigate to the following preferences group from the tree on the left: Input / Codecs -> Video codecs -> FFmpeg.  The right side of the preferences window will now change to "FFmpeg audio/video decoder".
  4. Check the "Allow speed tricks" checkbox
  5. Set the "Skip the loop filter for H.264 decoding" to "All"
  6. Click on the Save button on the bottom right of the preferences window.
Enjoy!

Friday, August 12, 2011

Mounting MSDOS/FAT filesystems under Solaris

I needed to copy over a bunch of photographs to my EON NAS so I put them on a USB stick and attached the stick directly to the NAS to get the maximum speed while copying.  It turns out, while on Linux you type something like:
mount -t vfat /dev/sdd1 /tmp/usbstick

to mount the FAT or FAT32 filesystem from /dev/sdd1 to /tmp/usbstick, that command doesn't work on Solaris which is what EON NAS is running on.  Here are all the steps I took to mount the USB stick under Solaris:
  • Run the "format" command to see the device name of the new USB stick.  The output looks like:
#formatSearching for disks... 
The current rpm value 0 is invalid, adjusting it to 3600done 
c3t0d0: configured with capacity of 465.74GB 
AVAILABLE DISK SELECTIONS: 0. c0t0d0
     /pci@0,0/pci1458,b002@11/disk@0,0 1. c0t1d0
     /pci@0,0/pci1458,b002@11/disk@1,0 2. c0t2d0
     /pci@0,0/pci1458,b002@11/disk@2,0 3. c0t3d0
     /pci@0,0/pci1458,b002@11/disk@3,0 4. c3t0d0
     /pci@0,0/pci1458,5004@13,2/storage@5/disk@0,0 
Specify disk (enter its number): ^C

Use Ctrl-C to break out of the format command.  Based on the output of the format command, I know my Seagate FreeAgentGoFlex USB drive is /dev/dsk/c3t0d0.
  • Create a mount point for that USB stick using:
mkdir /tmp/usbstick"
  • Mount the FAT filesystem on the first partition of /dev/dsk/c3t0d0 using the command:
mount -F pcfs /dev/dsk/c3t0d0s0:c /tmp/usbstick

Et voila!  You should have the disk mounted and writable.  Finish copying to/from the disk and then issue a umount /tmp/usbstick command to unmount.  Don't forget to clean up and remove the /tmp/usbstick directory.

Saturday, April 30, 2011

cwRsync, Windows 7 and UNIX Targets

Lately whenever I've had to rsync anything to my E O N-based NAS from my Windows 7 machine, I've had permissions issues on the NAS.  Specifically, any sub-directories I sync over are created with ridiculous permissions e.g. 0500 or something odd.  No files are able to be transferred until I manually login to the NAS and run a something similar to:

find -type d -exec chmod 0755 {} \;

That got annoying very quickly.

I came across the rsync --no-perms flag which alleviated the problem to some extent in that the directories were at least writable during the initial transfer but the permissions still had to be resolved before transferring anything else over.

I'd gotten used to the whole issue but then came across this post and self-researched answer by karikas that talked about exactly my situation.  Turns out it's a known issue of cygwin (which is what the cwRsync application is using) and Windows 7.  The solution is to set the following environment variable in Windows 7:

set CYGWIN=nontsec

Monday, August 30, 2010

CSV Exports in Rails

If you're looking for an elegant way to generate CSV files from your index views (or search views or anything else for that matter) you should look no further than this post on StackOverflow by rwc9u.

The gist is to add a format line in the respond_to section of the index method in your controller that caters to CSV.  Then create an index.csv.erb file where you can generate the actual CSV using inline ruby like you would for e.g. your index.html.erb view.  Actually retrieving the data in CSV format from your application involves adding a ".csv" extension to the end of your normal index path e.g. /test/widgets becomes /test/widgets.csv to return CSV.

Keep in mind that you'll need to restart your rails server if you create the initializer file that he suggests.

Friday, August 27, 2010

Exception handling for Net::SSH

I'm writing a bit of Ruby automation code which requires me to connect to multiple servers using ssh and gather specific information from them using a loop like so:

hostList.each do |h|
  Net::SSH.start(h, "user", :password => "password", :timeout => 10) do |ssh|
  end
end


However, my test script kept dying at various points due to issues with the ssh connection to specific servers.

Naturally I thought of wrapping the Net::SSH.start call in a begin/rescue/end but couldn't for the life of me find any information about the exceptions that the start method could throw.  Finally after a bit of digging on google I came across this page which details them rather handily :-)  In short, here's how I have it wrapped now:

hostList.each do |h| 
  begin
    Net::SSH.start(h, "user", :password => "password", :timeout => 10) do |ssh|
    end 
  rescue Timeout::Error
    puts "  Timed out"
  rescue Errno::EHOSTUNREACH
    puts "  Host unreachable"
  rescue Errno::ECONNREFUSED
    puts "  Connection refused"
  rescue Net::SSH::AuthenticationFailed
    puts "  Authentication failure"
  end
end


This works fabulously since I don't really need to handle the exceptions but would like to know about them.

Monday, May 3, 2010

Building a Diskless MythTV Frontend with Mythbuntu 10.04 - Lucid Lynx - Part 1

I just upgraded to Mythbuntu 10.04 which was released just a few days back on April 29th.  Since I was already running Mythbuntu 9.10 on my MythTV backend server I was able to upgrade it to Lucid using the well documented and fairly simple steps on the ubuntu.com site:

$ sudo apt-get install update-manager-core
$ sudo do-release-upgrade

After upgrading the backend server I decided to finally figure out why my diskless frontend server wasn't working.  After a day of investigating it turned out to be a faulty stick of memory.  With that replaced, I needed to configure a diskless frontend on the MythTV backend server so I could use PXE to boot into a frontend without having to worry about anything else.  Since Mythbuntu hasn't had a graphical control panel to create a diskless frontend since Karmic, this blog post documents everything I had to do to get my diskless frontend up and running.  Some of the setup already existed but I'm documenting it here for future use.

Requirements for PXE Booting
PXE is a way for computers to boot up using resources found on the network.  On some computers you have to press F12 or some other key to get them to PXE boot but on all computers that have network cards that support PXE you should be able to set the first boot option in the BIOS to something like "Network" to allow your computer to PXE boot.
During a PXE boot a network card will perform the following tasks without any intervention from any OS stored anywhere on the computer:
  1. automatically obtain an IP address,
  2. automatically obtain a boot kernel and initial ramdisk, and
  3. boot up the computer using that kernel and initial ramdisk 
After booting up the kernel using the initial ramdisk in step 3, the OS can either use a built-in hard drive or the computer's RAM as a root disk or it can NFS mount a volume from a remote server and use it as a root disk.  The NFS scenario is most common for computers that PXE boot and is the one I use.

For a computer to be able to automatically obtain an IP address you need a DHCP server in your network.  However, this DHCP server has to be configured so it can inform the PXE booting computer where the kernel and initial ramdisk files are located.  The kernel and initial ramdisk files are hosted on a TFTP server.  Finally the NFS mounted root disk needs an NFS server which will export the directory of files that will become the root directory of a diskless MythTV frontend.

In my home network I have a separate DHCP server hosted on a Linux machine that used to be my main Linux server.  Now it just serves DHCP until I can move that functionality into my Cisco router.  The other two pieces of the puzzle i.e. the TFTP server and the NFS server are handled by the Mythbuntu backend server and are closely tied to each other.  However, there is no reason why they can't be split up onto their own servers.

The next post will focus on the steps needed to build a diskless image using the Linux Terminal Server Project's utilities.  The post after that will focus on configuring the individual servers needed to put it all together.  These three posts should serve as a more or less complete guide to setting up a diskless Mythbuntu frontend.

Saturday, March 20, 2010

Switching your Rails Database from SQLite3 to PostgreSQL or MySQL

I deployed a minimal new Rails application I wrote over a period of a couple of hours and started testing it with valid production data.  It's basically a very simple CRUD application for a very specific audience.  Very basic stress testing (using Apache's ab) showed me that it didn't work well when requests were coming in with concurrency > 2. I realized I'd started the project using SQLite3 as the database and that's probably where the bottleneck is since it can handle requests with a concurrency of 1 just fine.  The application is deployed using mod_passenger (my first experience with it) and is configured to never tear down application instances due to idle timeouts.  With 6 application instances listening, a concurrency of 6 should have been a cinch.

In any case, now that I need to switch it to using a real database like PostgreSQL or MySQL, there were no obvious solutions that would allow me to keep the data I already had in my production database.  Everything out there talks about going from development to test to production each of which environments gets their own schema but nothing else.  Migrations allow you to keep your production data in place but that's not the same as dumping it and loading it.  Using database-specific dump/load utilities might result in SQL that needs to be tweaked before it can be loaded into another database type.

In comes the Yaml Db plugin by Orion Henry and Adam Wiggins.  It does exactly what one would expect.  Similar to the way schema.db is database-agnostic, the Yaml Db has a database-agnostic dump format and a similar load format.  One of the specific use cases mentioned on that site is "One common use would be to switch your data from one database backend to another."

Excellent!  Btw, thank you George for the tip!  You know who you are :)

Monday, March 15, 2010

Spaces in /etc/fstab

I needed to mount a Samba share from my windows gaming / media center PC to my MythTV backend so I could navigate to all my videos from a single location instead of having to worry about which server they were on.  To that end I shared out the Videos folder from my account on the Windows PC.  Since my user on the Windows PC is "Shahbaz Javeed" that presented a problem when trying to auto-mount it using fstab on my MythTV host - spaces aren't allowed in any field of /etc/fstab.  Any spaces are considered field delimiters.  The solution is to escape all spaces with \040.  My /etc/fstab entry now looks like so:

//frey/Users/Shahbaz\040Javeed/Videos   /var/lib/mythtv/videos/frey cifs     guest,ro       0       0

This works swimmingly!

Tuesday, December 29, 2009

CheckPoint VPN-1 SecureClient on Snow Leopard

It turns out that the CheckPoint VPN-1 SecureClient for Leopard (OSX 10.5) doesn't work on Snow Leopard (OSX 10.6) due to differences between the two versions in the kernel and the way kextload works.Harald has a blog entry detailing how to fix the Leopard package so it installs on Snow Leopard and then fix the installed files so they properly run as well.  He alludes to a method to fix the package itself so you can install it on multiple Macs without manually making those changes.

I took it upon myself to modify the package and am providing it here.  Feel free to use it at your own risk.  You can verify the authenticity of the file by running the following command to get its checksum:

cksum SecureClient-VPN-1.zip


You should get the following output:


3505974925 22321216 SecureClient-VPN-1.zip

Sunday, December 20, 2009

Boot Camp x64 is Unsupported on this Computer Model

Apple says that only certain models of Mac are x64 compatible when using Bootcamp 3.0 that comes with Snow Leopard and possibly Leopard.  Imagine my surprise when my 17" MacBook Pro wasn't one of them.  I'd hoped their flagship portable would be on the list.  I got the somewhat curt error message "Boot Camp x64 is Unsupported on this Computer Model" and the Bootcamp installer refused to continue.  The solution turned out to be simple.  I located the bootcamp64.msi file on the Snow Leopard disc and ran it in Vista compatibility mode.  Everything installed just fine.  So there, Apple!

Tuesday, December 15, 2009

OSX and /etc/resolv.conf

I recently went back to a Mac laptop and encountered an interesting issue.  I needed to make some changes to /etc/resolv.conf to reflect a modified search path and since /etc/resolv.conf is a symlink to /private/etc/resolv.conf I edited the latter file.  All was well until I connected to a different network.  Now my /private/etc/resolv.conf file, which clearly states that it's an auto-generated file, wasn't updated resulting in the "host" command (among other things) breaking.  After posting on Apple's forums I ended up answering my own question.

/private/etc/resolv.conf is itself a symlink to /var/run/resolv.conf which is the file that is auto-generated.  I ended up discovering that after looking at the /private/etc/resolv.conf file in my oldest Time Machine backup.  That'll learn me.

Tuesday, November 10, 2009

Accessing the KDE Wallet from the Cmdline

I needed to write a script that would contact my Exchange server at work via IMAP and list all the messages in the Calendar folder. The idea was to see if it was possible to perform a one-way sync from the Exchange server to a specific calendar in Kontact. I was going to embed my IMAP password in the script - security hole, I know - but we have a password policy that requires the password to change every 30 days. Since I didn't want to edit the script every month I decided to see if it was possible to use the password for kmail that's already stored in my KDE wallet.

I didn't have any luck finding a perl interface to the KDE wallet. However, thanks to the good folks at #kde on freenode, I found that the KDE wallet - and lots of other applications as well - expose their interfaces over D-Bus. This was the first time I'd dealt with D-Bus so it took some getting used to but I figured out how to read my kmail password from my KDE wallet. KDE comes with the handy qdbus program that allows command-line testing of the D-Bus interface.

What follows are step-by-step instructions on how to use qdbus to open your KDE wallet and read your kmail password. I'll incorporate this into my one-way sync experiment using the Net::DBus perl module but I wanted to put this out there in case someone else was looking at that.

Introduction to qdbus
A quick intro to qdbus before we get started so you can explore other options instead of just kwalletd:

The following command shows all applications exposing a DBus interface:

$ qdbus

:1.50

org.gtk.vfs.Daemon
:1.51
:1.52
:1.54
org.kde.kwalletd
:1.56

org.kde.printer-applet-3206
:1.57
net.update-notifier-kde-3203
:1.58

The numbers and strings refer to applications, however since most applications expose a recognizable string it's common to use just the strings and ignore the numbers.

The following command lists all the DBus paths exposed by the kwalletd application:

$ qdbus org.kde.kwalletd
/
/MainApplication
/modules
/modules/kwalletd


The /MainApplication path is mainly used when you want to interact with the application itself and you'll find many applications that expose a /MainApplication path. I haven't explored this much but it looks like it should be interesting.

With that introduction to qdbus you should have enough to explore further on your own.

Getting a Password from a KDE Wallet
The following steps will open your default KDE wallet and get your kmail password. Each step will have an explanation, the command issued and the output of that command.

We will use the /modules/kwalletd path in the DBus interface for org.kde.kwalletd for all our password-getting needs. You can get a list of all the methods and signals exposed in the /modules/kwalletd path by using the following command:

$ qdbus org.kde.kwalletd /modules/kwalletd

method bool org.kde.KWallet.isOpen(QString wallet)
method bool org.kde.KWallet.isOpen(int handle)
method bool org.kde.KWallet.keyDoesNotExist(QString wallet, QString folder, QString key)
method QString org.kde.KWallet.localWallet()

method QString org.kde.KWallet.networkWallet()
method int org.kde.KWallet.open(QString wallet, qlonglong wId, QString appid)
method int org.kde.KWallet.openAsync(QString wallet, qlonglong wId, QString appid, bool handleSession)
method int org.kde.KWallet.openPath(QString path, qlonglong wId, QString appid)

Formatting's a bit messed up going forward. Not sure why :-(

However, let's get to work obtaining the password. First, we will open the default KDE wallet - called kdewallet. We will call the org.kde.KWallet.open method which expects a wallet name string, what appears to be a wallet id (similar to a file handle it seems) and finally an application id string. We will use "kdewallet" as the wallet name since that's the name of the default wallet in KDE. We don't know the value of the wallet id so we'll just specify 0. The application id is interesting because KDE wallet prompts the currently logged in user with the application id of any applications that call the org.kde.KWallet.open method which we're abbreviating to just open since that uniquely identifies it in the method list for the /modules/kwalletd path. Specifying a meaningful id here goes a long way to helping the user click on "Allow", "Allow Once" or "Allow Never". With all that in mind, let's use the following command:

$ qdbus org.kde.kwalletd /modules/kwalletd org.kde.KWallet.open kdewallet 0 "KOrganizer-Exchange 1-way Sync"

470467109

This results in popping up a dialog box like so:


For the purpose of these experiments, I chose "Allow Once". Once I've allowed it, the qdbus call returns a wallet id - similar to a file handle - that we'll use in all our other method calls. I did see that if I waited too long the dialog box remains visible but qdbus times out. However, the next time you make the
org.kde.KWallet.open call it returns a valid wallet id without prompting which means the permission grant is persistent. I'll have to deal with the timeouts in my perl code somehow. The next step is to see what's stored in my wallet. This isn't strictly necessary if you already know what you want but serves to walk through my own discovery process. Notice I'm passing in the newly given wallet id as well as the full application id as I sent earlier.

$ qdbus org.kde.kwalletd /modules/kwalletd folderList 470467109 "KOrganizer-Exchange 1-way Sync"

AdobeAIR
Amarok
Form Data
Network Management Passwords
bilbo
kblogger
kmail
mailtransports


Let's list the contents of the kmail folder:

$ qdbus org.kde.kwalletd /modules/kwalletd entryList 470467109 kmail "KOrganizer-Exchange 1-way Sync"

account-242017858
account-990222852


I know the account-242017858 account is the one I need the password from because the other account is older. So let's see how to retrieve that password:

$ qdbus org.kde.kwalletd /modules/kwalletd readPasswordList 470467109 kmail account-242017858 "KOrganizer-Exchange 1-way Sync"

account-242017858: [the password here]


There you go folks! That's all it takes. Please let me know if you found this helpful.

Monday, November 9, 2009

Mythbuntu 9.10 Diskless Frontend

With Mythbuntu 9.10 out (simultaneously with Ubuntu 9.10), apparently the "Diskless Server" plugin for the Mythbuntu Control Center is missing. According to this thread on the Ubuntu forums, it's because the developer who was working on that has had to step away from it for the moment. However, manually building the diskless client/server setup still works. That same thread has all the relevant information. Thanks blackoper!

Saturday, November 7, 2009

Seagate FreeAgent USB Drives and Linux

I don't particularly like the Seagate FreeAgent line of drives. Ever since I tried the first one - a 500GB specimen - and it died on me while still connected to a machine running CentOS. One moment it was fine, the next it was gone or remounted read-only. I figured out it happened whenever the drive went idle. I've stayed away from them ever since.

Lately, however, I had to work with a FreeAgent drive again and this time I found this solution to the problem by trolav that uses the power of udev and sysfs to keep the drive working whether or not it's idle. While the solution is posted on an Ubuntu forum, it works well on CentOS 5 as well.

Friday, October 30, 2009

Determining 64-bitness of your CPU

It looks like /proc/cpuinfo isn't the only way to find out whether your CPU is 64bit capable. In an effort to determine the most reliable way to find out this information I came across this page. A quick summary:

If you see any output from the following command, you're running a 64-bit capable CPU.
grep ^flags /proc/cpuinfo | grep ' lm '
The following command, if it exists on your system, will tell you the width of your physical and logical CPUs:
lshw -C cpu | grep width
The lshw command is available natively on my Ubuntu 9.04 system and is available from rpmforge.org for RHEL5 and CentOS5 systems.

Friday, October 9, 2009

More Fun with PostgreSQL Date/Time

I got a number of comments from sasha2048 about the modulo, division and remainder operators for the interval data types in a previous blog entry. After playing with all the suggestions I figured it would be best to devote another blog post to the revised code for the functions and operators. The main quibble sasha2048 had with the functions was their precision - they were only good for intervals expressed in seconds and weren't able to handle more precise intervals e.g. in the millisecond range. Here, then, are the updated functions that have the following features:

  1. The concept of a modulo operator for double precision numbers where a % b = (a - floor(a/b)*b)
  2. Updated interval_divide and interval_modulo functions that store the extracted epoch from a timestamp into a double precision variable instead of an integer
  3. Made all functions immutable and "return null on null input"
  4. Added a default value for the "precision" argument in the round function - it's now set to 1 second so unless you specify a precision level, all round calls will round an interval to the nearest second.

-- Functions

create function interval_divide (interval, interval) returns double precision as $$

declare

firstEpoch constant double precision := extract(epoch from $1);

secondEpoch constant double precision := extract(epoch from $2);

begin

return firstEpoch / secondEpoch;

end

$$ language plpgsql immutable returns null on null input;

create function double_precision_modulo (double precision, double precision) returns integer as $$

begin

return ($1 - floor($1 / $2) * $2);

end

$$ language plpgsql immutable returns null on null input;

create function interval_modulo (interval, interval) returns interval as $$

declare

firstEpoch constant double precision := extract(epoch from $1);

secondEpoch constant double precision := extract(epoch from $2);

begin

return (firstEpoch % secondEpoch) * '1 second'::interval;

end

$$ language plpgsql immutable returns null on null input;

create function round (interval, interval default '1 second'::interval) returns interval as $$

declare

quantumNumber constant double precision := round($1 / $2);

begin

return $2 * quantumNumber;

end

$$ language plpgsql immutable returns null on null input;

-- Operators

create operator % (

leftarg = double precision,

rightarg = double precision,

procedure = double_precision_modulo

);

create operator / (

leftarg = interval,

rightarg = interval,

procedure = interval_divide

);

create operator % (

leftarg = interval,

rightarg = interval,

procedure = interval_modulo

);