Tuesday, 28 August 2012

Nokia N9 Bluetooth PAN, USB & Dummy Networks

Please note: All of these instructions assume you have developer mode enabled and are familiar with using the Linux console. One of the variants of dummy networking I present here also requires a package to be installed with Inception or use of an open-mode kernel to disable aegis. I present an alternative method to use a pseudo-dummy network for people who do not wish to do that.

Background

Earlier this year I bought a Nokia N9 (then took it in for service TWICE due to a defective GPS, then returned it for a refund since Nokia had returned it un-repaired both times, then bought a new one for $200 less than I originally paid, then bought a second for my fiancé).

The SIM card I use in the N9 is a pretty basic TPG $1/month deal, which is fine for the small amount of voice calls I make, but it's 50MB of data per month is a not really enough, so I'd like it to use alternative networks wherever possible.

When working on another computer with an Internet connection, I could simply hook up the N9 via USB networking and have the computer give it a route to the Internet. That works well, but has the problem that any applications using the N9's Internet Connectivity framework (anything designed for the platform is supposed to do this via libconic) would not know that there was an Internet connection and would refuse to work - so I had to find a way to convince them that there was an active Internet connection using a dummy network. Also, this obviously wouldn't work when I was away from a computer.

I also happen to carry a pure data SIM card in my Optus MyTab with me all the time (being my primary Internet connection), so when I'm on the go I'd like to be able to connect to the Internet on the N9 via the tablet rather than use the small amount of data from the TPG SIM.

The MyTab is running CyanogenMod 7 (I'm not a fan of Android, but at $130 to try it out the price was right), so I am able to switch on the WiFi tethering on the tablet and connect that way, but it has a couple of problems:

  • It needs to be manually activated before use
  • It needs to be manually deactivated to allow the bluetooth tethering to work
  • It isn't very stable (holding a wakelock helps a lot - the terminal application can be used for this purpose)
  • It's a bit of a battery drain (at least the tablet has a huge battery)

The MyTab also supports tethering over bluetooth PAN (which I regularly use at home), so it made a lot of sense to me to connect the N9 to the tablet using that as well when I am out and about. Unfortunately, the N9 does not come with any software to connect to a bluetooth network, and I couldn't manage to find anyone else who had successfully done this (There are a couple of threads discussing it).

Fortunately, the N9 has a normal Linux userspace under the hood (one reason I'd take this over Android any day), which includes bluez 4.x and as such I was able to use that to make it do bluetooth PAN.

USB Network

Let's start with USB Networking since it is already supported on the N9 and works out of the box once developer mode is enabled (select SDK mode when plugging in).

Here's a few tricks you can do to streamline the process of using the USB network to gain an Internet connection. You will also want to follow the steps under one of the Dummy Networking sections below to allow applications (such as the web browser) to use it.

On the host, add this section to your /etc/network/interfaces (this is for Debian based distributions, if you use something else you will have work out the equivalent):

allow-hotplug usb0
iface usb0 inet static
    address 192.168.2.14
    netmask 255.255.255.0
    up iptables -t nat -I POSTROUTING -j MASQUERADE
    up iptables -A FORWARD -i usb0 -j ACCEPT
    up iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
    up echo 1 > /proc/sys/net/ipv4/ip_forward
    down echo 0 > /proc/sys/net/ipv4/ip_forward
    down iptables -F FORWARD
    down iptables -t nat -F POSTROUTING

Next, modify the same file on the N9 so that the usb0 section looks like this (this section already exists - I've just extended it a little):

auto usb0
iface usb0 inet static
    address 192.168.2.15
    netmask 255.255.255.0
    gateway 192.168.2.14
    up /usr/lib/sdk-connectivity-tool/usbdhcpd.sh 192.168.2.14
    down /usr/lib/sdk-connectivity-tool/usbdhcpd.sh stop
    up echo nameserver 208.67.222.222 >> /var/run/resolv.conf
    up echo nameserver 208.67.220.220 >> /var/run/resolv.conf
    down rm /var/run/resolv.conf

Now whenever you plug in the N9 and choose SDK mode it should automatically get an Internet connection with no further interaction required and you should be able to ping hosts on the Internet :)

But, you will probably notice that most applications (like the web browser) will still bring up the "Connect to internet" dialog whenever you use them and will refuse to work. To make these applications work we need to create a dummy network that they can "connect" to, while in reality they actually use the USB network.

    USB Networking Notes:
  • The iptables commands on the host will alter the firewall and routing rules to allow the N9 to connect to the Internet through the host. If you use your own firewall with other forwarding rules you may want to remove those lines and add the appropriate rules to your firewall instead.
  • The above commands will turn off all forwarding on the host and purge the FORWARD and POSTROUTING tables when the N9 is unplugged - if your host is a router for other things you definitely will want to remove those lines.
  • The two IP addresses used for the DNS lookups on the N9 are those of OpenDNS.org - you might want to replace them with some other appropriate servers. OpenDNS should be accessible from any Internet connection, which is why I chose them.
  • The N9 will use the most recently modified file under /var/run/resolv.conf* (specifically those listed in /etc/dnsmasq.conf) for DNS lookups. Which means that connecting to a WiFi/3G network AFTER bringing up the USB network would override the DNS settings. I suggest setting the DNS settings for your dummy network to match to avoid that problem.
  • The N9 doesn't run the down rules when it should, rather they seem to be delayed until the USB cable is plugged in again, when they are run immediately before the up rules. Because of the previous note, this isn't really an issue for the dnsmasq update, but it may be an issue if you wanted to do something more advanced.
  • Alternatively, there is an icd2 plugin for USB networking for the N900 available on gitorious. I haven't had a look at this yet to see if it works on the N9 or how it compares to the above technique. This would require installation with Inception.

Dummy Network

This approach to setting up a dummy network isn't for everyone. You are going to need to compile a package in the Harmattan platform SDK (or bug me to upload the one I built somewhere) and install it on the device with Inception, or use an open mode kernel. If you don't feel comfortable with this, you might prefer to use the technique discussed in the Alternative Dummy Network section instead.

First grab the dummy icd plugin from https://maemo.gitorious.org/icd2-network-modules

[host]$ cd /scratchbox/users/$USER/home/$USER
[host]$ git clone git://gitorious.org/icd2-network-modules/libicd-network-dummy.git
[host]$ scratchbox
[sbox]$ sb-menu
 Select -> HARMATTAN_ARMEL
[sbox]$ cd libicd-network-dummy
[sbox]$ dpkg-buildpackage -rfakeroot

Now copy /scratchbox/users/$USER/home/$USER/libicd-network-dummy_0.14_armel.deb to the N9, then install and configure it on the N9 with:

[N9]$ /usr/sbin/incept libicd-network-dummy_0.14_armel.deb

[N9]$ gconftool-2 -s -t string /system/osso/connectivity/IAP/DUMMY/type DUMMY
[N9]$ gconftool-2 -s -t string /system/osso/connectivity/IAP/DUMMY/name 'Dummy network'

[N9]$ devel-su
[N9]# /sbin/initctl restart xsession/icd2

Next time the connect to Internet dialog appears you should see a new entry called 'Dummy network' that you can "connect" to so that everything thinks there is an Internet connection, while they really use your USB or bluetooth connection.

Alternative Dummy Network

This isn't ideal in that it enables the WiFi & creates a network that nearby people can see, but it does have the advantage that it works out of the box and does not require Inception or Open Mode.

Open up settings -> internet connection -> create new connection

Fill out the settings like this:

Connection name: dummy
Network Name (SSID): dummy
Use Automatically: No
network mode: ad hoc
Security method: None

Under Advanced settings, fill out these:

Auto-retrieve IP address: No
IP address: 0.0.0.0
Subnet mask: 0.0.0.0
Default gateway: 0.0.0.0

Auto-retrieve DNS address: No
Primary DNS address: 208.67.222.222
Secondary DNS address: 208.67.220.220

These are the OpenDNS.org DNS servers - feel free to substitute your own.

Then if the 'Connect to internet' dialog comes up you can connect to 'dummy', which will satisfy that while leaving your real USB/bluetooth network alone.

Bluetooth Personal Area Networking (PAN)

This is very much a work in progress that I hope to polish up and eventually package up and turn into an icd2 plugin so that it will nicely integrate into the N9's internet connectivity framework.

First thing's first - you will need to enable the bluetooth PAN plugin on the N9, by finding the line DisabledPlugins in /etc/bluetooth/main.conf and removing 'network' from the list so that it looks something like:

[General]

# List of plugins that should not be loaded on bluetoothd startup
# DisablePlugins = network,hal
DisablePlugins = hal

# Default adaper name
...

Then restart bluetooth by running:

[N9]$ devel-su
[N9]# /sbin/initctl restart xsession/bluetoothd

Until I package this up more nicely you will need to download my bluetooth tethering script from:

https://raw.github.com/DarkStarSword/junk/master/blue-tether.py

You will need to edit the dev_dbaddr in the script to match the bluetooth device you are connecting to. Note that I will almost certainly change this to read from a config file in the very near future, so you should double check the instructions in the script first.

Put the modified script on the N9 under /home/user/blue-tether.py

You first will need to pair with the device you are connecting to in the N9's bluetooth GUI like usual.

Once paired, you may run the script from the terminal with develsh -c ./blue-tether.py

The bluetooth connection will remain up until you press enter in the terminal window. Currently it does not detect if the connection goes away, so you would need to restart it in that case.

For convenience you may create a desktop entry for it by creating a file under /usr/share/applications/blue-tether.desktop with this contents:

[Desktop Entry]
Type=Application
Name=Blue Net
Categories=System;
Exec=invoker --type=e /usr/bin/meego-terminal -n -e develsh -c /home/user/blue-tether.py
Icon=icon-m-bluetooth-lan

Again, this is very much an active work in progress - expect to see a packaged version soon, and hopefully an icd2 plugin before not too long.

One Outstanding Graphical Niggle

You may have noticed that the dummy plugin doesn't have it's own icon - in the connect to Internet dialog it seems to pick a random icon, and once connected the status bar displays it as though it was a cellular data connection. As far as I can tell, the icons (and other connectivity related GUI elements) are selected by /usr/lib/conniaptype/lib*iaptype.so which is loaded by /usr/lib/libconinetdui.so which is in turn used by /usr/bin/sysuid. I haven't managed to find any API references or documentation for these and I suspect being part of Nokia's GUI that they fall into the firmly closed source side of Harmattan. This would be nice to do properly if I want to create my own icd2 plugins, so if anyone has some pointers for this, please leave a note in the comments.

Why is Inception required for real dummy networking?

Well, it's because the Internet Connectivity Daemon requests CAP::sys_module (i.e. The capability to load kernel modules):

~ $ ariadne sh
Password for 'root':

/home/user # accli -I -b /usr/sbin/icd2
Credentials:
        UID::root
        GID::root
        CAP::kill
        CAP::net_bind_service
        CAP::net_admin
        CAP::net_raw
        CAP::ipc_lock
        CAP::sys_module
        SRC::com.nokia.maemo
        AID::com.nokia.maemo.icd2.
        icd2::icd2
        icd2::icd2-plugin
        Cellular

Because of this, aegis will only allow it to load libraries that originated from a source that has the ability to grant CAP::sys_module, which unfortunately (but understandably given what the capability allows) is only the system firmware by default, so attempting to load it would result in this (in dmesg):

credp: icd2: credential 0::16 not present in source SRC::9990007
Aegis: credp_kcheck failed 9990007 libicd_network_dummy.so
Aegis: libicd_network_dummy.so verification failed (source origin check)

Ideally the developers would have thought of this and separated the kernel module loading out into a separate daemon so that icd2 would not require this credential and therefore would allow third-party plugins to be loaded, but since that is not the case we have to use Inception to install the dummy plugin from a source that has the ability to grant the same permissions that the system firmware enjoys (Note that the library does not actually request any permissions because libraries always inherit the permissions of the binary that loaded them - it just needs to have come from a source that could have granted it that permission).

Also, if anyone could clarify what the icd2::icd2-plugin credential is for I would appreciate it - I feel like I've missed something because it's purpose as documented (to load icd2 plugins) seems rather pointless to me (icd2 loads libraries based on gconf settings, which it can do just as well without this permission... so what is the point of this?).

Thursday, 23 February 2012

Tiling tmux Keybindings

When most people use a computer, they are are using either a compositing or stacking window manager - which basically means that windows can overlap. The major alternative to this model is known as a tiling window manager, where the window manager lays out and sizes windows such that they do not overlap each other.

I started using a tiling window manager called wmii some years ago after buying a 7" EeePC netbook and trying to find alternative software more suited to the characteristics of that machine. Most of the software I ended up using on that machine I now use on all of my Linux boxes, because I found that it suits my workflow so much better.

Wmii as a window manager primarily focuses on organising windows into tags (like multiple desktops) and columns. Within a column windows can either be sized evenly, or a single window can take up the whole height of the column, optionally with the title bars of the other windows visible (think minimised windows on steroids).

Wmii is very heavily keyboard driven (which is one of it's strengths from my point of view), though a mouse can be used for basic navigation as well. It is also heavily extensible with scripting languages and in fact almost all interactions with the window manager are actually driven by the script. It defaults to using a shell script, but also ships with equivalent python and ruby scripts (the base functionality is the same in each), and is easy to extend.

By default keyboard shortcuts provide ways to navigate left and right between columns, up and down between windows within a column, and to switch between 10 numbered tags (more tags are possible, but rarely needed). Moving a window is as simple as holding down shift while performing the same key combos used to navigate, and columns and tags are automatically created as needed (moving a window to the right of the rightmost column would create a new column for example), and automatically destroyed when no longer used.

Recent versions of wmii also work really well with multiple monitors (though there is still some room for improvement in this area) allowing windows to really easily be moved between monitors with the same shortcuts used to move windows between columns (and they way it differentiates between creating a new column on the right of the left monitor versus moving the window to the right monitor is pure genius).

Naturally with such a powerful window manager, I want to use it to manage all my windows and all my shells. The problem with this of course is SSH - specifically, when I have many remote shells open at the same time and what happens when the network goes away. You see, I've been opening a new terminal and SSH connection for each remote shell so I can use wmii to manage them, which works really great until I need to suspend my laptop or unplug it to go to a meeting, then have to spend some time re-establishing each session, getting it back to the right working directory, etc. And, I've lost the shell history specific to each terminal.

Normally people would start screen on the remote server if they expect their session to go away, and screen can also manage a number of shells simultaneously, which would be great... except that it is no where near as good at managing those shells as wmii can manage windows and if I'm going to switch it would need to be pretty darn close.

I've been aware for some time of an alternative to screen called tmux which seemed to be much more sane and feature-rich than screen, so the other day I decided to see if I could configure tmux to be a realistic option for managing many shells on a remote machine that I could detach and re-attach from when suspending my laptop.

Tmux supports multiple sessions, "windows" (like tags in wmii), and "panes" (like windows in wmii). I managed to come up with the below configuration file which sets up a bunch of keybindings similar to the ones I use in wmii (but using the Alt modifier instead of the Windows key) to move windows... err... "panes" and to navigate between them.

Unlike wmii, tmux is not focussed around columns, which technically gives it more flexibility in how the panes are arranged, but sacrifices some of the precision that the column focus gives wmii (in this regard tmux is more similar to some of the other tiling window managers available).

None of these shortcut keys need to have the tmux prefix key pressed first, as that would have defeated the whole point of this exercise:

Alt + ' - Split window vertically *
Alt + Shift + ' - Split window horizontally

Alt + h/j/k/l - Navigate left/down/up/right between panes within a window
Alt + Shift + h/j/k/l - Swap window with the one before or after it **

Alt + Ctrl + h/j/k/l - Resize pane *** - NOTE: Since many environments use Ctrl+Alt+L to lock the screen, you may want to change these to use the arrow keys instead.

Alt + number - Switch to this tag... err... "window" number, creating it if it doesn't already exist.
Alt + Shift + number - Send the currently selected pane to this window number, creating it if it doesn't already exist.

Alt + d - Tile all panes **
Alt + s - Make selected pane take up the maximum height and tile other panes off to the side **
Alt + m - Make selected pane take up the maximum width and tile other panes below **

Alt + f - Make the current pane take up the full window (actually, break it out into a new window). Reverse with Alt + Shift + number **

Alt + PageUp - Scroll pane back one page and enter copy mode. Release the alt and keep pressing page up/down to scroll and press enter when done.

* Win+Enter opens a new terminal in wmii, but Alt+Enter is already used by xterm, so I picked the key next to it

** These don't mirror the corresponding wmii bindings because I could find no exact equivalent, so I tried to make them do something similar and sensible instead.

*** By default there is no shortcut key to resize windows in wmii (though the python version of the wmiirc script provides a resize mode which is similar), so I added some to my scripts.


~/.tmux.conf (Download Latest Version Here)

# Split + spawn new shell:
# I would have used enter like wmii, but xterm already uses that, so I use the
# key next to it.
bind-key -n M-"'" split-window -v
bind-key -n M-'"' split-window -h

# Select panes:
bind-key -n M-h select-pane -L
bind-key -n M-j select-pane -D
bind-key -n M-k select-pane -U
bind-key -n M-l select-pane -R

# Move panes:
# These aren't quite what I want, as they *swap* panes *numerically* instead of
# *moving* the pane in a specified *direction*, but they will do for now.
bind-key -n M-H swap-pane -U
bind-key -n M-J swap-pane -D
bind-key -n M-K swap-pane -U
bind-key -n M-L swap-pane -D

# Resize panes (Note: Ctrl+Alt+L conflicts with the lock screen shortcut in
# many environments - you may want to consider the below alternative shortcuts
# for resizing instead):
bind-key -n M-C-h resize-pane -L
bind-key -n M-C-j resize-pane -D
bind-key -n M-C-k resize-pane -U
bind-key -n M-C-l resize-pane -R

# Alternative resize panes keys without ctrl+alt+l conflict:
# bind-key -n M-C-Left resize-pane -L
# bind-key -n M-C-Down resize-pane -D
# bind-key -n M-C-Up resize-pane -U
# bind-key -n M-C-Right resize-pane -R

# Window navigation (Oh, how I would like a for loop right now...):
bind-key -n M-0 if-shell "tmux list-windows|grep ^0" "select-window -t 0" "new-window -t 0"
bind-key -n M-1 if-shell "tmux list-windows|grep ^1" "select-window -t 1" "new-window -t 1"
bind-key -n M-2 if-shell "tmux list-windows|grep ^2" "select-window -t 2" "new-window -t 2"
bind-key -n M-3 if-shell "tmux list-windows|grep ^3" "select-window -t 3" "new-window -t 3"
bind-key -n M-4 if-shell "tmux list-windows|grep ^4" "select-window -t 4" "new-window -t 4"
bind-key -n M-5 if-shell "tmux list-windows|grep ^5" "select-window -t 5" "new-window -t 5"
bind-key -n M-6 if-shell "tmux list-windows|grep ^6" "select-window -t 6" "new-window -t 6"
bind-key -n M-7 if-shell "tmux list-windows|grep ^7" "select-window -t 7" "new-window -t 7"
bind-key -n M-8 if-shell "tmux list-windows|grep ^8" "select-window -t 8" "new-window -t 8"
bind-key -n M-9 if-shell "tmux list-windows|grep ^9" "select-window -t 9" "new-window -t 9"

# Window moving (the sleep 0.1 here is a hack, anyone know a better way?):
bind-key -n M-')' if-shell "tmux list-windows|grep ^0" "join-pane -d -t :0" "new-window -d -t 0 'sleep 0.1' \; join-pane -d -t :0"
bind-key -n M-'!' if-shell "tmux list-windows|grep ^1" "join-pane -d -t :1" "new-window -d -t 1 'sleep 0.1' \; join-pane -d -t :1"
bind-key -n M-'@' if-shell "tmux list-windows|grep ^2" "join-pane -d -t :2" "new-window -d -t 2 'sleep 0.1' \; join-pane -d -t :2"
bind-key -n M-'#' if-shell "tmux list-windows|grep ^3" "join-pane -d -t :3" "new-window -d -t 3 'sleep 0.1' \; join-pane -d -t :3"
bind-key -n M-'$' if-shell "tmux list-windows|grep ^4" "join-pane -d -t :4" "new-window -d -t 4 'sleep 0.1' \; join-pane -d -t :4"
bind-key -n M-'%' if-shell "tmux list-windows|grep ^5" "join-pane -d -t :5" "new-window -d -t 5 'sleep 0.1' \; join-pane -d -t :5"
bind-key -n M-'^' if-shell "tmux list-windows|grep ^6" "join-pane -d -t :6" "new-window -d -t 6 'sleep 0.1' \; join-pane -d -t :6"
bind-key -n M-'&' if-shell "tmux list-windows|grep ^7" "join-pane -d -t :7" "new-window -d -t 7 'sleep 0.1' \; join-pane -d -t :7"
bind-key -n M-'*' if-shell "tmux list-windows|grep ^8" "join-pane -d -t :8" "new-window -d -t 8 'sleep 0.1' \; join-pane -d -t :8"
bind-key -n M-'(' if-shell "tmux list-windows|grep ^9" "join-pane -d -t :9" "new-window -d -t 9 'sleep 0.1' \; join-pane -d -t :9"

# Set default window number to 1 instead of 0 for easier key combos:
set-option -g base-index 1

# Pane layouts (these use the same shortcut keys as wmii for similar actions,
# but don't really mirror it's behaviour):
bind-key -n M-d select-layout tiled
bind-key -n M-s select-layout main-vertical \; swap-pane -s 0
bind-key -n M-m select-layout main-horizontal \; swap-pane -s 0

# Make pane full-screen:
bind-key -n M-f break-pane
# This isn't right, it should go back where it came from:
# bind-key -n M-F join-pane -t :0

# We can't use shift+PageUp, so use Alt+PageUp then release Alt to keep
# scrolling:
bind-key -n M-PageUp copy-mode -u

# Don't interfere with vi keybindings:
set-option -s escape-time 0

# Enable mouse. Mostly to make selecting text within a pane not also grab pane
# borders or text from other panes. Unfortunately, tmux' mouse handling leaves
# something to be desired - no double/tripple click support to select a
# word/line, all mouse buttons are intercepted (middle click = I want to paste
# damnit!), no automatic X selection integration(*)...
set-window-option -g mode-mouse on
set-window-option -g mouse-select-pane on
set-window-option -g mouse-resize-pane on
set-window-option -g mouse-select-window on

# (*) This enables integration with the clipboard via termcap extensions. This
# relies on the terminal emulator passing this on to X, so to make this work
# you will need to edit your X resources to allow it - details below.
set-option -s set-clipboard on


You may also need to alter your ~/.Xresources file to make some things work (this is for xterm):

~/.Xresources (My Personal Version)

/* Make Alt+x shortcuts work in xterm */
XTerm*.metaSendsEscape: true
UXTerm*.metaSendsEscape: true

/* Allow tmux to set X selections (ie, the clipboard) */
XTerm*.disallowedWindowOps: 20,21,SetXprop
UXTerm*.disallowedWindowOps: 20,21,SetXprop

/* For some reason, this gets cleared when reloading this file: */
*customization: -color

To reload this file without logging out and back in, run:
xrdb ~/.Xresources

There's a pretty good chance that I'll continue to tweak this, so I'll try to update this post anytime I add something cool.

Edit 27/02/2012: Added mouse & clipboard integration & covered changes to .Xresources file.

Friday, 17 February 2012

SSH passwordless login WITHOUT public keys

I was recently in a situation where I needed SSH & rsync over SSH be to able to log into a remote site without prompting for a password (as it was being called from within a script and would have been non-trivial to make the script pass in a password, especially as OpenBSD-SSH does not provide a trivial mechanism for scripts to pass in passwords - see below).

Normally in this situation one would generate a public / private keypair and use that to log in without a prompt, either by leaving the private key unencrypted (ie, not protected by a passphrase), or by loading the private key into an SSH agent prior to attempting to log in (e.g. with ssh-add).

Unfortunately the server in question did not respect my ~/.ssh/authorized_keys file, so public key authentication was not an option (boo).


Well, it turns out that you can pre-authenticate SSH sessions such that an already open session is used to authenticate new sessions (actually, new sessions are basically tunnelled over the existing connection).

The option in question needs a couple of things set up to work, and it isn't obviously documented as a way to allow passwordless authentication - I had read the man page multiple times and hadn't realised what it could do until Mikey at work pointed it out to me.

To get this to work you first need to create (or modify) your ~/.ssh/config as follows:

Host *
  ControlPath ~/.ssh/master_%h_%p_%r


Now, manually connect to the host with the -M flag to ssh and enter your password as normal:

ssh -M user@host

Now, as long as you leave that connection open, further normal connections (without the -M flag) will use that connection instead of creating their own one, and will not require authentication.


Edit:
Note that you may instead edit your ~/.ssh/config as follows to have SSH always create and use Master connections automatically without having to specify -M. However, some people like to manually specify when to use shared connections so that the bandwidth between the low latency interactive sessions and high throughput upload/download sessions doesn't mix as that can have a huge impact on the interactive session.

Host *
  ControlPath ~/.ssh/master_%h_%p_%r

  ControlMaster auto



Alternate method, possibly useful for scripting


Another method I was looking at using was specifying a program to return the password in the SSH_ASKPASS environment variable. Unfortunately, this environment variable is only used in some rare circumstances (namely, when no tty is present, such as when a GUI program calls SSH or rsync), and would not normally be used when running SSH from a terminal (or in the script as I was doing).

Once I found out about the -M option I stopped pursuing this line of thinking, but it may be useful in a script if the above pre-authentication method is not practical (perhaps for unattended machines).

To make SSH respect the SSH_ASKPASS environment variable when running from a terminal, I wrote a small LD_PRELOAD library libnotty.so that intercepts calls to open("/dev/tty") and causes them to fail.

If anyone is interested, the code for this is in my junk repository (libnotty.so & notty.sh). You will also need a small script that echos the password (I hope it goes without saying that you should check the permissions on it) and point the SSH_ASKPASS environment variable to it.

https://github.com/DarkStarSword/junk

Git trick: Deleting non-ancestor tags

Today I cloned the git tree for the pandaboard kernel, only to find that it didn't include the various kernel version tags from upstream, so running things like git describe or git log v3.0.. didn't work.

My first thought was to fetch just the tags from an upstream copy of the Linux kernel I had on my local machine:

git fetch -t ~/linus

Unfortunately I hadn't thought that though very well, as that local tree also contained all the tags from the linux-next tree, the tip tree as well as a whole bunch more from various distro trees and several other random ones, which I didn't want cluttering up my copy of the pandaboard kernel tree.

This lead me to try to find a way to delete all the non-ancestor tags (compared to the current branch) to simplify the tree. This may be useful to others to remove unused objects and make the tree smaller after a git gc -- that didn't factor into my needs as I had specified ~/linus to git clone with --reference so the objects were being shared.

Anyway, this is the script I came up with, note that this only compares the tags with the ancestors of the *current HEAD*, so you should be careful that you are on a branch with all the tags you want to keep first. Alternatively you could modify this script to collate the ancestor tags of every local/remote branch first, though this is left as an exercise for the reader.


#!/bin/sh

ancestor_tags=$(mktemp)
echo -n Looking up ancestor tags...\ 
git log --simplify-by-decoration --pretty='%H' > $ancestor_tags
echo done.

for tag in $(git tag --list); do
 echo -n "$tag"
 commit=$(git show "$tag" | awk '/^commit [0-9a-f]+$/ {print $2}' | head -n 1)
 echo -n ...\ 
 if [ -z "$commit" ]; then
  echo has no commit, deleting...
  git tag -d "$tag"
  continue
 fi
 if grep $commit $ancestor_tags > /dev/null; then
  echo is an ancestor
 else
  echo is not an ancestor, deleting...
  git tag -d "$tag"
 fi
done

rm -fv $ancestor_tags


Also note that this may still leave unwanted tags in if they are a direct ancestor of the current HEAD - for instance, I found a bunch of tags from the tip tree had remained afterwards, but they were much more manageable to delete with a simple for loop and a pattern.