Monday, December 27, 2010

See USB/SD Card Files on CR-48 Chrome OS Laptop

One of the first things I tried when I received my CR-48 laptop from Google was to pop in a SD card and try to send a jpeg image via Gmail.  This didn't work at all since Chrome OS doesn't support that functionality (yet.)  I did manage to find a work-around though.  First, the CR-48 must be put in developer mode by removing the piece of black tape next to the battery connector and flipping the switch under that tape.  (When you do this, the CR-48 will re-initialize on your first boot after you flip the switch which will take about 20 or so minutes.  Also, you will have to hit CTRL-D every time you boot the CR-48 to bypass the error screen that you get when the machine is in developer mode.)  Now, do the following to allow easy access to USB drives or SD Cards from the Chrome OS file picker (the one that comes up when you need to upload a file via a web page.)

1.  Enter "about:flags" (without the quotes) in the Chrome address bar.
2.  Enable the "Advanced File System" experimental feature.  (This will require a Chrome restart.)
3.  Open a terminal on the CR-48 by entering the CTRL-ALT-t key sequence.
4.  At the crosh command prompt, type in "shell" (without the quotes) to get a bash shell.
5.  Enter the command "cd /home/chronos/user/Downloads/" (without the quotes.)
6.  Enter the command "ln -s /media" (without the quotes.)
7.  Use the ALT-TAB key sequence or the window switcher button to return to Chrome.

That's it - now if you plug in a SD Card or USB drive, you can now attach a file to Gmail (or upload via any other web form) by choosing the "File Shelf" directory which will now let you go to Downloads->Media where you will see your SD Card or USB device mounted.  This seems to work well for most USB storage devices/SD cards, but it appears that Chrome OS will only find the first partition on such devices.  (It even came in handy for uploading the picture for this post!)

Saturday, August 21, 2010

PHP Script for Adding Entries to ConnectBot

Here's a quick little script that lets you add entries via a PHP script to your ConnectBot Android application.  This really helps if you are like me and have over 50 hosts that you want to add.  Using this is easier if you have root, but it is still possible to use even if you don't have root.  First you'll have to either hardcode your connections into the connection_entries array in the script, or modify the script to load your entries rom a csv, database, etc.  For best results, make sure ConnectBot isn't running when you update the hosts database or your changes might not be saved.  The Connectbot hosts database has the following columns:
  • username
    • Self explanatory, your username on the host.
  • port
    • 22 for ssh, etc.
  • hostkeyalgo
    • ssh-rsa, ssh-dsa, etc.
  • color
    • Row color for this new entry (different colors don't seem to work to well when you have a large number of hosts though.)
  • usekeys
    • true or false based on whether you are using a ssh key to login or a password.
  • use authagent
    • yes or no based on whether or not you want to enable the auth agent forwarding (yes, strange it isn't true/false also.)
  • pubkeyid
    • integer representing the key from "Manage Pubkeys" (if any) - is in the same order as the keys are displayed in "Manage Pubkeys", starting with 1.
  • delkey
    • 'del' or other key to use as delete key.
  • fontsize
    • 10 or other integer.
  • wantsession
    • true unless you are using this entry for port forwarding.
  • compression
    • false (use true if you are on a slow network.)
  • encoding
    • 'UTF-8' or any of the others supported by ConnectBot.
  • stayconnected
    • 0 or 1 based on if you want ConnectBot to attempt to reconnect when disconnected.
Instructions with root:
  • Run "php connectbot_hosts_update.php get hosts" to retrieve the ConnectBot connections database.  (Must have adb.)
  • Run "php connectbot_hosts_update.php view hosts" to view the existing connection entries.
  • Make a backup of the hosts file :-).
  • Run "php connectbot_hosts_update.php update hosts" to update the database.
  • Run "php connectbot_hosts_update.php put hosts" to add the new entries to Connectbot.  (Must have adb.)
Instructions without root:
  • Via terminal on Android device or adb, run the command "cat /data/data/org.connectbot/databases/hosts > /sdcard/hosts" then "adb pull /sdcard/hosts hosts".
  • Run "php connectbot_hosts_update.php view hosts" to view the existing connection entries.
  • Make a backup of the hosts file :-).
  • Run "php connectbot_hosts_update.php update hosts" to update the database.
  •  Run the command "adb push hosts /sdcard/hosts" then (via terminal on Android device or adb,) run the command "cat /sdcard/hosts > /data/data/org.connectbot/databases/hosts".
OK, if that hasn't scared you off yet, then here's the code!

# connectbot_hosts.php - manage connectbot hosts from PHP
# 2010 - Alex Shillington
# Note: doesn't support port forwards (yet)

$action     = $argv[1];
$dbfile     = $argv[2];

if ($action == 'view' && $dbfile != '') {
} else if ($action == 'update' && $dbfile != '') {
} else if ($action == 'get') {
} else if ($action == 'put' && $dbfile != '') {
} else {
    print "Usage:\nphp connectbot_hosts.php view|update|get|put HOST_DBFILE\n";
    print "(HOST_DBFILE will default to 'hosts' if not specified for get and put options.)\n";

function updateData($dbfile) {
    # change defaults as desired (view existing database to see current values)
    $defaults = array(
        'username'       => 'myusername',
        'port'           => '22',
        'hostkeyalgo'    => 'ssh-rsa',
        'color'          => 'gray',
        'usekeys'        => 'true',
        'useauthagent'   => 'no',
        'pubkeyid'       => '1', #note: this is in the same order as they are displayed in "Manage Pubkeys", starting with 1
        'delkey'         => 'del',
        'fontsize'       => 10,
        'wantsession'    => 'true',
        'compression'    => 'false',
        'encoding'       => 'UTF-8',
        'stayconnected'  => 0

    $entries = array();

    # define connection entries
    $connection_entries = array();
    $connection_entries[] = array('username'=>'alex','nickname'=>'server1_remote','hostname'=>'','port'=>22,'pubkeyid'=>3);
    $connection_entries[] = array('username'=>'alex','nickname'=>'server1_local','hostname'=>'','port'=>22,'pubkeyid'=>3);
    # etc...

    # add any other code here that you want to add to connection_entries array from other csv files, db, etc.
    # ...

    # populate entries from connection_entries and default entry values
    foreach ($connection_entries as $connection) {
        $entry = $defaults;
        foreach ($connection as $var=>$val) {
            $entry[$var] = $val;
        $entries[] = $entry;

    # connect to hosts db
    $dbh = connect($dbfile);

    # add entries to hosts db
    foreach ($entries as $entry) {
        $sql = "SELECT _id,hostname FROM hosts WHERE nickname = '{$entry['nickname']}'";
        if ($sth = $dbh->query($sql)) {
            $results = $sth->fetchAll(PDO::FETCH_ASSOC);
            $_id = $results[0]['_id'];
            # overwrite existing entry with same nickname
            if ($_id != '' || $entry['force'] == 1) {
                $sql = "DELETE FROM hosts WHERE _id = $_id";
                try {
                } catch(Exception $e) {
                    print "ERROR: " . $e->getMessage() . "\n$sql\n";
            $sql = "SELECT _id,hostname FROM hosts WHERE hostname = '{$entry['hostname']}'";
            if ($sth = $dbh->query($sql)) {
                $results = $sth->fetchAll(PDO::FETCH_ASSOC);
                $_id = $results[0]['_id'];
                if ($_id != '' || $entry['force'] == 1) {
                    $sql = "DELETE FROM hosts WHERE _id = $_id";
                    try {
                    } catch(Exception $e) {
                        print "ERROR: " . $e->getMessage() . "\n$sql\n";
                if ($_id == '' || $entry['force'] == 1) {
                    print "{$entry['nickname']} is new {$entry['hostname']}\n";
                    $keystring = implode(',',array_keys($entry));
                    foreach ($entry as $key=>$val) {
                        $entry[$key] = "'" . sqlite_escape_string($val) . "'";
                    $valstring = implode(',',array_values($entry));
                    $sql = "INSERT INTO hosts ($keystring) VALUES ($valstring)";
                    try {
                    } catch(Exception $e) {
                        print "ERROR: " . $e->getMessage() . "\n$sql\n";
                } else {
                    print "{$entry['nickname']} is new {$entry['hostname']}, but has an existing entry with a different nickname - not adding\n";
        } else {
            print "Error in query:\n$sql\n";
    $dbh = null;


function viewData($dbfile) {
    $dbh = connect($dbfile);
    $sql = "SELECT * FROM hosts";
    if ($sth = $dbh->query($sql)) {
        $results = $sth->fetchAll(PDO::FETCH_ASSOC);
        print implode(',',array_keys($results[0])) . "\n";
        foreach ($results as $result) {
            print implode(',',$result) . "\n";
    } else {
        print "error reading from hosts db $dbfile\n";
    $dbh = null;

function connect($dbfile) {
    try {
        $dbh = new PDO("sqlite:$dbfile");
    } catch(Exception $e) {
        print "ERROR: " . $e->getMessage() . "\n";
    return $dbh;

function getHosts($dbfile = 'hosts') {
    if (!`./adb pull /data/data/org.connectbot/databases/hosts ./$dbfile`) {
        print "sorry, you need root to get the hosts file that way\n";
        print "try this from a script instead:\n";
        print "cat /data/data/org.connectbot/databases/hosts > /sdcard/hosts\n";
        print "then adb pull that file\n";

function putHosts ($dbfile = 'hosts') {
    if (!`./adb push $dbfile /data/data/org.connectbot/databases/hosts`) {
        print "sorry, you need root to push that way\n";
        print "instead, push $dbfile to /sdcard/hosts then:\n";
        print "cat /sdcard/hosts > /data/data/org.connectbot/databases/hosts\n";
    print "\nIf connectbot is running, don't save any config changes once you've pushed the new hosts database.  Also, you might need to kill connectbot or restart the phone to see the new hosts.\n";


Friday, August 13, 2010

Tethering Saves the Day!

Wow, got server updates to do tonight and a storm knocks out our phone and DSL...  Tethering to the rescue!  (In my case, android-wifi-tether -  Definitely a must have whether it's a 3rd party app like this one, or one that comes with your phone from the carrier (at extra cost.)

Tuesday, July 27, 2010

Nice New Battery Charger From Apple

Here's a nice little new product from Apple today that you might have missed amongst the other, bigger updates:

Nice, slim AA battery charger that comes with 6 rechargeable AA batteries - not a bad deal!

Monday, July 26, 2010

Quick Volvo A/C Fix

From the quick and dirty hack dept... Many 7xx/9xx Volvos experience the "Fan slows down when accelerating" issue.  What is really happening is not that the fan is slowing down, but that the vacuum servos that control the flaps start leaking.  Since there is little to no vacuum available while accelerating, these cars use a check-valve to ensure that the vacuum available to the climate control vacuum solenoids doesn't leak back out to the manifold during acceleration.  Assuming that the one-way check valve (mounted on the firewall) checks out OK, the most likely culprit is a leaking vacuum servo.  These are located under the dashboard on the drivers side (plus a hard to access one for recirc behind the glove box.)  These are easy to test with a hand-held vacuum pump and gauge.  If the servo doesn't hold vacuum, it's bad.  The one that went bad on mine was the servo with the "double" diaphragm - the only one that has 2 vacuum hoses attached.  This had a orange vacuum hose attached to the side and a blue vacuum hose on top.  The diaphragm that the orange hose connected to was the one that was bad on mine.  All I had to do was plug the hose with a rubber plug, and I'm a happy (and much cooler) camper in this heat.  I did lose the "floor only" fan setting (can only get floor + defrost now,) but for me, that is much better than attempting the extremely labor-intensive operation to replace the vacuum servo.  (Either via the "proper" way that involves removing basically the entire dash, or the "quick" way here: )  Here's a picture of this quick fix:

...then I just stuck the vacuum line back on the tab on the servo to keep it in place:

Not bad for free and about 30 minutes of testing/removing & replacing panels!

Tuesday, July 13, 2010

Set Up Unison on Ubuntu to Sync 2 Laptops

Unison Sync Between Two Machines Part 1

The problem:

You have two laptops (or desktops, whatever) that you switch between or have multiple users share.  You want your data to sync between the two machines in both directions (you can save a file on either machine and it will sync to the other.)

Answer Number 1:

You can use Ubuntu one if you have less than 2 GB of data to keep up with (or are willing to pay to sync more data.)  Ubuntu One is nice since you can also access the files you are syncing via a web interface.  My biggest problem with Ubuntu One is the sync delay, it seems to sync whenever it feels like it (or sometimes not.)

Answer Number 2:

Unison ( lets you sync directories much easier than setting up rsync, especially in this use case.  (On Ubuntu, installation is as simple as "aptitude install unison".)  There is a graphical UI - you can start it once to create the $HOME/.unison/ directory and $HOME/.unison/default.prf file if you wish, or create those by hand.   (The default.prf file is empty to start with.)  I don't bother with the UI, but it's pretty self explanatory to use if you desire.  Let's say we want to keep our $HOME/Documents/, $HOME/Music/, $HOME/Pictures/, and $HOME/Desktop/ directories synchronized.  Install unison on both machines, then add the following to the $HOME/.unison/default.prf file:

# Unison preferences file
root = /home/alex
root = ssh://

terse = true
perms = -1
owner = true
group = true
times = true
batch = true

path = Desktop
path = Documents
path = Music
path = Pictures

(I would recommend creating some test directories to test all of this with before you use it on any important data.)  Once you've got the default.prf file set up on both machines, all you need to do is run the unison command to sync.  You will want to be sure to run it manually once before running it from CRON or in any other automated way since you'll have to confirm some things.  Automating unison is then as simple as adding a CRON entry such as:

# m h  dom mon dow   command
0 3 * * * /usr/bin/unison 2>&1 >> /home/$USER/.unison/unison_cron.log

Coming up in future parts of this article:
  • Setting up a root CRON for shared or other non-user directories
  • Setting up unison to sync at login and logout on Ubuntu

Thursday, July 1, 2010

Quick Linux Idle Time Hack

Here's an excerpt from a post I made about a bug on the Timekpr project (  This, or some variation on it could come in handy if you need to keep track of idle time on a Linux X session:

I found the code for an executable named idle at - I modified it somewhat to just return the number of seconds of idle time for that display. Here's the C code:

#include <scrnsaver.h>
#include <stdio.h>
main() {
  XScreenSaverInfo *info = XScreenSaverAll
  Display *display = XOpenDisplay(NULL);
  if (display != NULL) {
rQueryInfo(display, DefaultRootWindow(display), info);
    printf("%lu", info->idle/1000);
  } else {
    puts ("error");

I compiled that with the command "gcc -o idle idle.c -lXss" and put the resulting binary in /usr/local/sbin/idle. You call that by first exporting the display that you want to query idle time for as $DISPLAY, then you run idle. For example, say that you have users logged in to displays :5 and :6, then you can run the following commands (as root):

export DISPLAY=:5.0;/usr/local/sbin/idle
export DISPLAY=:6.0;/usr/local/sbin/idle

to find the number of seconds that those users have been idle (no keyboard/mouse activity.) For timekpr, what we really need to find are the users that are active (the normal users command returns all users that are logged in, active or not.) For this, I wrote a shell script and put that in /usr/local/sbin/ - here's the code for that:

if [ "$(id -u)" != "0" ]; then
   echo "This script must be run as root" 1>&2
   exit 1
for i in `w | grep tty | awk '{print $2 $3 ":" $1}' | sed 's/^tty[0-9]*\://g'`; do
    display=`echo $i | awk -F ':' '{print $1}'`
    user=`echo $i | awk -F ':' '{print $2}'`
`export DISPLAY=$display;/usr/local/sbin/idle`
    case "$1" in
      echo "$user is on display $display with idletime $idletime"
            if [ $idletime -lt 10 ]; then
      echo -n "$user "

As root, when you run /usr/local/sbin/, you will get only a listing of users that are currently active (in a gnome session only - this doesn't help you are trying to limit time for ssh users...)

Wednesday, June 30, 2010

*Sigh* Sometimes it's the little things

I was setting up my new laptop (running Ubuntu 10.04 on a Lenovo G555) and installed all of the necessary packages for playing DVDs, but I kept getting authorization errors...  Well, it turns out that every time I've set up Linux to play DVDs before, it's always been on a well used computer that's already played DVDs before, and this laptop never had a region set for the DVD drive.  That's where the regionset command comes in!  Running regionset shows the current region (or in my case, that no region was set yet) and allows you to change the region:

$ regionset
regionset version 0.1 -- reads/sets region code on DVD drives
Current Region Code settings:
RPC Phase: II
type: SET
vendor resets available: 4
user controlled changes resets available: 4
drive plays discs from region(s): 1, mask=0xFE

Would you like to change the region setting of your drive? [y/n]:n

That's it - make sure you have a CD or DVD in the drive, then just answer 'y' if you wish to change the region, then input the number of the region you wish to set it to!

Sunday, June 27, 2010

LOL! Aeron With Wheels of Doom!


Android GPS Shootout!

(Upper Left: Motorola Droid, Upper Right: Sprint EVO 4G,Lower Left: T-Mobile MyTouch Slide, Lower Right: T-Mobile G1)

I decided to do a side by side comparision of the GPS function on the T-Mobile G1, Verizon Motorola Droid, Sprint HTC EVO 4G, and the T-Mobile MyTouch 3G Slide.  I used the Android application "GPS Test" by Chartcross Ltd. to do the tests.  Here's a breakdown of the results:

T-Mobile G1Verizon Motorola DroidSprint HTC EVO 4GT-Mobile MyTouch 3G Slide
OSCyanogenMod 5.0.8CyanogenMod 5.0.8Stock OSSlideM
(Table: shows average, minimum and maximum measured times to get a GPS position.)

The time I measured was the time it took from turning on the GPS to the time that the GPS Test application showed a non-zero figure for accuracy.  All phones were tested from a fresh boot with no applications running that use GPS and weren't tested until they had been on for 20 minutes.  The most surprising result here is that the G1 seems to significantly outperform the other phones in the time it takes to get an initial GPS lock.  Since these tests were all performed during the course of 1 hour, I suspect that more valid results would be obtained from daily tests, over say, the course of a month - I don't think I'm going to do that...  There seemed to be no real variability between the accuracy in feet measured by the phones - they all fluctuated between 5-9 satellites in view and 6.6ft to typically no more than 52.5 feet in accuracy.  This test answered the question I was asking myself, which was "is there any real difference in the GPS performance of these phones?"  I'd say the answer is "no" because the results are close enough to each other that, when you take into consideration all of the factors at play with GPS, there isn't really a big difference between these phones.  I look forward to any thoughts from readers in the comments.

Hello World

f1rst p0st!!!
I yam what I yam!