Articles tagged: linux

Keyboard navigation in Emacs GDB mode

I love Emacs GDB mode, but I always found it annoying that there is no given key binding (or function which could be directly mapped into a key binding) for switching between the different views given by gdb-many-windows. The usual window and buffer switching functions are insufficient because the GDB windows are flagged as dedicated (so switch-buffer refuses to swap them in place), and in the case of the Locals/Registers and Breakpoints/Threads windows, the buffer you want to visit doesn’t necessarily even exist until you click that button.

This went from mildly annoying to a major headache when I needed to run the debugger in a remote Emacs session, over SSH from Mac OS X’s Terminal.app which does not support xterm mouse emulation. So I wrote this gdb-select-window function and corresponding key bindings to finally get the desired behavior…

;; For the consistency of gdb-select-window's calling convention...
(defun gdb-comint-buffer-name ()
  (buffer-name gud-comint-buffer))
(defun gdb-source-buffer-name ()
  (buffer-name (window-buffer gdb-source-window)))

(defun gdb-select-window (header)
  "Switch directly to the specified GDB window.
Moves the cursor to the requested window, switching between
`gdb-many-windows' \"tabs\" if necessary in order to get there.

Recognized window header names are: 'comint, 'locals, 'registers,
'stack, 'breakpoints, 'threads, and 'source."

  (interactive "Sheader: ")

  (let* ((header-alternate (case header
                             ('locals      'registers)
                             ('registers   'locals)
                             ('breakpoints 'threads)
                             ('threads     'breakpoints)))
         (buffer (intern (concat "gdb-" (symbol-name header) "-buffer")))
         (buffer-names (mapcar (lambda (header)
                                 (funcall (intern (concat "gdb-"
                                                          (symbol-name header)
                                                          "-buffer-name"))))
                               (if (null header-alternate)
                                   (list header)
                                 (list header header-alternate))))
         (window (if (eql header 'source)
                     gdb-source-window
                   (or (get-buffer-window (car buffer-names))
                       (when (not (null (cadr buffer-names)))
                         (get-buffer-window (cadr buffer-names)))))))

    (when (not (null window))
      (let ((was-dedicated (window-dedicated-p window)))
        (select-window window)
        (set-window-dedicated-p window nil)
        (when (member header '(locals registers breakpoints threads))
          (switch-to-buffer (gdb-get-buffer-create buffer))
          (setq header-line-format (gdb-set-header buffer)))
        (set-window-dedicated-p window was-dedicated))
      t)))

;; Use global keybindings for the window selection functions so that they
;; work from the source window too...
(mapcar (lambda (setting)
          (lexical-let ((key    (car setting))
                        (header (cdr setting)))
            (global-set-key (concat "\C-c\C-g" key) #'(lambda ()
                                                        (interactive)
                                                        (gdb-select-window header)))))
        '(("c" . comint)
          ("l" . locals)
          ("r" . registers)
          ("u" . source)
          ("s" . stack)
          ("b" . breakpoints)
          ("t" . threads)))

Put this in your ~/.emacs.el or init.el and fire up gdb-many-windows, and then you’ll be able to quickly switch between the GDB windows with the following keyboard shortcuts:

  • C-c C-g c — comint (GDB command) buffer
  • C-c C-g l — locals buffer
  • C-c C-g r — registers buffer
  • C-c C-g u — source window
  • C-c C-g s — stack buffer
  • C-c C-g b — breakpoints buffer
  • C-c C-g t — threads buffer

PXE booting OpenBSD on an ALIX via Ubuntu Live CD

Update: I’ve expanded the contents of this post into a full guide to running an OpenBSD router on an ALIX board.

This is a quick guide to booting the OpenBSD installer on a PC Engines ALIX board with tinyBIOS (such as the ALIX 2d3) via PXE, using just the following:

  • PC with two network interfaces. One of these needs to be Ethernet, and the other must connect to the Internet. For example, any standard PC laptop with both WiFi and Ethernet adapters will work if there’s a WiFi Internet connection available.
  • Null modem cable
  • Ethernet crossover cable
  • USB-serial adapter (unless your PC has a built-in RS-232 port)
  • Ubuntu Linux 10.10 desktop live CD

Thanks to the versatility of the Ubuntu live CD (specifically the use of AUFS to provide a writable root directory in RAM), you can set up the necessary PXE boot server without making any permanent changes to your PC.

Ubuntu packages

Boot the Ubuntu live CD and quit the installer. Ensure that Ubuntu has a working Internet connection, then enable the “universe” package repository by uncommenting the corresponding lines in /etc/apt/sources.list. Now open a terminal and run the following commands to install prerequisite packages:

$ sudo -s
# apt-get update
# apt-get install dhcp3-server tftpd xinetd cu

Network configuration & NAT

Run this command to configure a static address on the Ethernet interface:

# ifconfig eth0 up 192.168.2.1 netmask 255.255.255.0

I’ve found you may also need to configure the static address in the “Network Connections” dialog (under Preferences in the System menu) to prevent Network Manager from getting in the way. This is sort of hackish, but we only need it to work for the duration of the install.

Now enable routing and configure a simple NAT using iptables so that the ALIX board can access the internet through your PC’s wireless connection:

# echo 1 > /proc/sys/net/ipv4/ip_forward
# iptables -t nat -A POSTROUTING -o wlan0 -j MASQUERADE
# iptables -A FORWARD -i eth0 -j ACCEPT
# iptables -A FORWARD -i wlan0 -m state --state RELATED,ESTABLISHED -j ACCEPT

Connect your PC’s Ethernet port to the first port on your ALIX board using the crossover cable. On the ALIX 2d3, the first port is the one adjacent to the USB ports.

DHCP server

Replace /etc/dhcp3/dhcpd.conf with the following contents:

authoritative;

shared-network LOCAL-NET {
    option domain-name-servers 8.8.8.8;

    subnet 192.168.2.0 netmask 255.255.255.0 {
        option routers 192.168.2.1;
    filename "pxeboot";
    range 192.168.2.100 192.168.2.200;
    default-lease-time 600;
    max-lease-time 7200;
    }
}

Also, edit the file /etc/default/dhcp3-server so that the last line reads:

INTERFACES="eth0"

Now you can start the DHCP server.

# /etc/init.d/dhcp3-server start

TFTP server

Create an xinetd file /etc/xinetd.d/tftp as:

service tftp
{
    socket_type = dgram
    protocol = udp
    wait = yes
    user = root
    server = /usr/sbin/in.tftpd
    server_args = -s /tftpboot
}

Next create the aforementioned directory /tftpboot. Download the files bsd.rd and pxeboot from the /4.8/i386/ directory on your favorite OpenBSD mirror and copy them into this directory.

Restart xinetd to load the new configuration.

# /etc/init.d/xinetd restart

Serial console

Connect your laptop’s serial port (or plugged-in USB-serial adapter) to the ALIX board’s serial port with your null modem cable, then use the cu command to connect to the serial console. For example, if you’re using a USB adapter and your ALIX’s BIOS has the default serial port settings:

# cu -e -o -s 38400 -l /dev/ttyUSB0

PXE boot

With your serial console ready, plug in the ALIX board’s power adapter, and you should see the board begin to boot. While the memory check is being performed, press the ‘s’ key to enter the tinyBIOS settings, and verify that PXE boot is enabled (if it isn’t, press ‘e’ to toggle it).

After exiting the BIOS settings menu, the board will reboot. It should find your PXE server and bring you to an OpenBSD boot menu. Enter the following at this menu:

boot> stty com0 38400
boot> set tty com0
boot> bsd.rd

The installer will boot from the bsd.rd image that you downloaded. Now perform the installation as normal, but remember to configure the serial port as your system console in the installer.

Firefox 3.5 on Debian Lenny AMD64

Now that Firefox 3.5 is out, I wanted to get it running on my 64-bit Debian 5.0 laptop—but there’s no 3.5 package in Sid yet, as of July 2, and mozilla.com only carries 32-bit binary tarballs for Linux. So I had to build Firefox 3.5 from source. Fortunately this turned out to be a lot less painful than I had imagined; this post will show you how I did it, in case you’re in a similar spot.

Build dependencies

The first thing to do is to make sure you’ve installed everything for the build process. Since you’re on a nice, civilized operating system like Debian, the following commands should have you covered:

$ sudo apt-get install build-essential libidl-dev autoconf2.13
$ sudo apt-get build-dep iceweasel

Obtaining the Mozilla source code

Mozilla’s official developer guide recommends downloading individual source archives for those who simply want to build a Firefox release, but I decided to get the source via Mercurial checkout, since this way there will be less to download and re-compile each time a security update comes along. The downside to this is that the initial download takes longer, and the full Mozilla source repository takes up about 686 MB on my hard drive.

First you’ll need to have the Mercurial source control manager installed on your computer, of course. If you don’t have it already, just type the following…

$ sudo apt-get install mercurial

Now change to whichever directory you want to keep the repository in, and run

$ hg clone http://hg.mozilla.org/releases/mozilla-1.9.1 \
mozilla-1.9.1

This will copy the full Firefox 3.5 / Mozilla 1.9.1 development branch to your computer. If you’re on a slow U.S. residential Internet connection like I am, this might be a good time to go do something else.

Once this is done, check out the particular Firefox 3.5 release that you’d like to build. Since there have not yet been any dot releases at the time of this writing, I just did:

$ cd mozilla-1.9.1
$ hg checkout -r FIREFOX_3_5_RELEASE

Configuring, building, and installing

The Mozilla build is controlled by a file called .mozconfig, which can live in your home directory, outside of the source tree. This makes it easy to “set” a particular Firefox build configuration, then reuse it repeatedly in the future without having to dig through old configure.log files or take notes elsewhere.

Create ~/.mozconfig with the following contents:

mk_add_options MOZ_OBJDIR=@TOPSRCDIR@/obj-firefox
ac_add_options --prefix=/opt/firefox
ac_add_options --enable-application=browser
ac_add_options --with-system-zlib
ac_add_options --with-system-jpeg
ac_add_options --enable-optimize
ac_add_options --enable-official-branding
ac_add_options --enable-canvas
ac_add_options --enable-strip
ac_add_options --disable-tests
ac_add_options --disable-installer
ac_add_options --disable-accessibility
ac_add_options --enable-xinerama
ac_add_options --with-default-mozilla-five-home=/usr/lib/firefox-3.5

Edit: Some problems have been reported with these particular configuration options; `see below`_ if this doesn’t work for you…

Now you’re ready to actually build Firefox—another good opportunity to go find something else to do for a little while:

$ make -f client.mk build

When the build is done, install it with

$ sudo make -f client.mk install

Close any running instances of Iceweasel, then try starting up your new browser:

$ /opt/firefox/bin/firefox

Later on: getting Firefox updates

Suppose that two weeks from now there is a security update release, Firefox 3.5.1. Since you already have a copy of the Mozilla source repository on your computer and you’ve already completed a full build, downloading and building any patch releases will take significantly less time. For a hypothetical Firefox 3.5.1 build, you would do:

$ hg pull
$ hg checkout -r FIREFOX_3_5_1_RELEASE
$ make -f client.mk build
$ sudo make -f client.mk install

And then restart your web browser.

Mobile LAN-oriented filtering in iptables

One of the things that I really like about pf, the OpenBSD firewall, is how it lets you define dynamic packet filtering rules — rules that filter based on your network interfaces’ current addresses at the time of filtering. For instance, if I want to allow SSH connections to my laptop only from my local network:

pass in on xl0 inet proto tcp from (xl0:network) to any \
port ssh flags S/SFRA

(xl0:network) is not resolved to a specific address block at configuration load time; if you switch networks — say, if you go from home to work — the rule’s behavior will change accordingly.

Unless I have overlooked some recent change in Linux, this cannot be achieved in a direct fashion with iptables. You can insert a rule to reject non-LAN source addresses, but such a rule is static. When you change network addresses, the rule must be explicitly updated.

In lieu of rewriting all of netfilter to accommodate this use case (*cough*), I just wrote a shell script to help mitigate the pain of manually updating my laptop’s firewall rules — merely a shortcut to cut down on the amount of typing I do on any given day, but if you tend to move around as much as I do, all those keystrokes can add up :) So with this script you can, in one fell swoop, start and open up global access to an SSH server:

# ssh-serve any

Or only allow local access from the networks you’re connected to:

# ssh-serve lan

Or only local network access on a specific interface:

# ssh-serve lan eth0

Or only access from a given set of IP addresses and/or CIDR blocks:

# ssh-serve addr 192.168.0.104 10.18.0.0/16

Better yet, you can make the whole process automagical by hooking into your Linux distribution’s DHCP client. For instance, in Ubuntu Hardy Heron you can automate ssh-serve by creating a file /etc/dhcp3/dhclient-exit-hooks.d/ssh-serve:

# Allow SSH access from your local network only, and keep these filter
# rules up-to-date as you move from one network to another.
case $reason in
BOUND|REBIND|REBOOT)
ssh-serve lan
;;
esac

This script was written (and named) with Secure Shell in mind, but it could just as easily govern over any other service controlled by a standard SysV init script. See below the jump for the code…

#!/bin/sh
# ssh-serve - Manage SSH server status and IP-based access control.
#
# Use this script to manage the state of the system's OpenSSH server, and
# the iptables rules allowing or denying remote access to it, in one fell
# swoop.  Synopsis:
#
# ssh-serve any
#   Start the server and allow access from anywhere.
#
# ssh-serve lan ( <iface-name> )*
#   Start the server and restrict access to clients connecting from from
#   local network addresses on one of any number of the computer's network
#   interfaces.  If no interface names are specified, then all non-loopback
#   interfaces listed by ifconfig will be provisioned for.
#
# ssh-serve addr ( <cidr> )+
#   Start the server and restrict access to clients connecting from one of
#   any number of specified IP addresses or CIDR blocks.
#
# ssh-serve off
#   Shut down the SSH server and close off access in the firewall.
#
# In order to use this, you will need to set up a separate iptables INPUT
# chain to which this script has exclusive write access; it will overwrite
# any other rules that may exist in its chain.  For example, you could do
# the following:
#
# $ sudo iptables -N SSH_ACTION
# $ sudo iptables -A SSH_ACTION -j REJECT
# $ sudo iptables -A INPUT -t tcp --dport 22 -m state --state NEW \
#   -j SSH_ACTION
#
# Then set the variable SSH_CHAIN to 'SSH_ACTION' in the configuration
# section below.
#
# Mark Shroyer
# Tue Sep 23 17:11:25 EDT 2008
### BEGIN CONFIGURATION ###################################################
# SSH connection logic iptables chain.  Only new, TCP port 22 connections
# in the INPUT table should be jumped to this chain.  WARNING: Any
# pre-existing rules in this chain will be overwritten by this script.
SSH_CHAIN=SSH_ACTION
# Jump target for accepted connections (typically ACCEPT)
JUMP_ACCEPT=SSH_ACCEPT
# Jump target for rejected connections (typically DROP or REJECT)
JUMP_REJECT=REJECT
# Where is our ssh init script?
SSH_INIT_SCRIPT=/etc/init.d/ssh
# Where is our iptables?
IPTABLES=iptables
# Where is our ifconfig?
IFCONFIG=ifconfig
### END CONFIGURATION #####################################################
run() {
echo $@
$@
}
usage() {
echo "Usage: $0 ( any | lan (<iface-name>)* | addr (<cidr>)+ | off )"
exit 1
}
mask_to_cidr() {
mask=$( echo "$1" | tr . ' ' )
sum=0
error=0
for part in $mask
do
case $part in
255) sum=$(( $sum+8 )) ;;
254) sum=$(( $sum+7 )) ;;
252) sum=$(( $sum+6 )) ;;
248) sum=$(( $sum+5 )) ;;
240) sum=$(( $sum+4 )) ;;
224) sum=$(( $sum+3 )) ;;
192) sum=$(( $sum+2 )) ;;
128) sum=$(( $sum+1 )) ;;
0) sum=$(( $sum )) ;;
*) error=1 ;;
esac
done
if [ $error -eq 0 ]
then
echo -n $sum
else
return 1
fi
}
ssh_serve_off() {
run $SSH_INIT_SCRIPT stop
run $IPTABLES -F $SSH_CHAIN
run $IPTABLES -A $SSH_CHAIN -j $JUMP_REJECT
}
ssh_serve_lan() {
ifaces=$( $IFCONFIG | awk '
/^[a-zA-Z0-9]+/ {
if ( $1 !~ /^lo[0-9]*$/ ) {
printf "%s ", $1;
}
}
' )
ifaddrs=''
for iface in $ifaces
do
info=$( $IFCONFIG $iface | awk '
/inet addr:/    { info=$0; }
/UP/            { up=1; }
/RUNNING/       { running=1; }
END             {
if ( up && running ) {
printf info;
}
}
' )
if [ $# -gt 1 ]
then
if ! echo " $@ " | grep -q " $iface "
then
continue
fi
fi
if [ ! -z "$info" ]
then
vals=$( echo "${info}" \
| sed -e 's/Bcast:[^\ ]\+//g' -e 's/[a-zA-Z]\+:\?//g' )
addr=$( echo "${vals}" | awk '{ printf "%s", $1; }' )
mask=$( echo "${vals}" | awk '{ printf "%s", $2; }' )
fi
if [ \( ! -z "$addr" \) -a \( ! -z "$mask" \) -a \( ! -z "$info" \) ]
then
cidr=$( mask_to_cidr $mask )
if [ $? -ne 0 ]
then
echo "CIDR conversion error on netmask ${mask}.  Aborting."
exit -1
fi
ifaddrs="${ifaddrs}${iface}:${addr}/${cidr} "
fi
done
run $IPTABLES -F $SSH_CHAIN
for ifaddr in $ifaddrs
do
iface=$( echo ${ifaddr} | awk 'BEGIN { FS=":"; } { printf "%s", $1 }' )
addr=$( echo ${ifaddr} | awk 'BEGIN { FS=":"; } { printf "%s", $2 }' )
run $IPTABLES -A $SSH_CHAIN -i $iface -s $addr -j $JUMP_ACCEPT
done
run $IPTABLES -A $SSH_CHAIN -j $JUMP_REJECT
run $SSH_INIT_SCRIPT start
}
ssh_serve_addr() {
if [ $# -lt 1 ]
then
usage
fi
run $IPTABLES -F $SSH_CHAIN
for addr in "$@"
do
run $IPTABLES -A $SSH_CHAIN -s "$addr" -j $JUMP_ACCEPT
done
run $IPTABLES -A $SSH_CHAIN -j $JUMP_REJECT
run $SSH_INIT_SCRIPT start
}
ssh_serve_any() {
run $IPTABLES -F $SSH_CHAIN
run $IPTABLES -A $SSH_CHAIN -j $JUMP_ACCEPT
run $SSH_INIT_SCRIPT start
}
command="$1"
if [ $# -gt 0 ]
then
shift
fi
case "$command" in
off|lan|addr|any)
ssh_serve_${command} $@
;;
*)
usage
;;
esac

Pagination