Archive for the ‘Tips’ Category

Creating a bootable USB memory stick with multiple GNU/Linux distros

Tuesday, July 13th, 2010

Packages needed: syslinux, cfdisk (or fdisk if you’re more comfortable with that), qemu (optional)

cd
dmesg # Look for USB device. Will probably be something like sdc.
sudo cfdisk /dev/sdc # WARNING! Make sure /dev/sdc is your memory stick before continuing!

Remove all partitions and create one new partition from the empty space. Set type to W95 FAT32 (0b), and set Bootable. Write and quit!

Create a fat32 filesystem on the memory stick

sudo mkfs.vfat /dev/sdc1
sudo mount /dev/sdc1 /mnt

Download a GNU/Linux dist iso, for example Debian Live

mkdir debian
wget http://cdimage.debian.org/cdimage/squeeze_live_alpha1/i386/iso-hybrid/debian-live-60alpha1-i386-standard.iso
sudo mount -o loop debian-live-60alpha1-i386-standard.iso debian
sudo cp -R debian/* /mnt
cd /mnt
sudo mv isolinux syslinux
cd syslinux
sudo mv isolinux.cfg syslinux.cfg
sudo sed -i 's/isolinux/syslinux/g' syslinux.cfg
sudo cp /usr/lib/syslinux/{vesa,}menu.c32 .

You should now have a working single dist bootable USB memory stick. Let’s try it

sudo umount /mnt
sudo qemu /dev/sdc # this should present you with the boot menu of the dist you used, in this example debian live

All well so far, clean up and prepare for adding another dist to the USB memory stick.

cd
sudo umount debian
rmdir debian

Adding a second dist to the USB memory stick. The following steps may be repeated with different dists until your memory stick is full!

mkdir puppy
wget http://ftp.nluug.nl/ftp/pub/os/Linux/distr/puppylinux/puppy-5.0.1/lupu-501.iso
sudo mount -o loop lupu-501.iso puppy
sudo mount /dev/sdc1 /mnt
sudo mkdir /mnt/puppy
sudo cp -R puppy/* /mnt/puppy

Now all we need to do is to add a menu entry for Puppy to the root syslinux menu

vim /mnt/syslinux/syslinux.cfg

You can view the syslinux.cfg (or isolinux.cfg) to see what the dist uses for entries, for Puppy it is:

label puppy
kernel vmlinuz
append initrd=initrd.gz pmedia=cd

Copy these lines and add them to the end of /mnt/syslinux/syslinux.cfg
Also, change the two last lines to say

kernel /puppy/vmlinuz
append initrd=/puppy/initrd.gz pmedia=cd

That’s it, you now have a bootable USB memory stick with two different linux live dists. To test it out just umount and qemu
Repeat the last steps (from “mkdir puppy”) to add more dists!

Update: corrections and emphasis on warnings

Document Freedom Seminar March 31

Wednesday, March 24th, 2010
Liberate your documents!

Free seminar on free software and document freedom on Document Freedom Day, March 31 at 7PM.

To honor Document Freedom Day, we are proud to announce that there will be a seminar at Gnutiken in Gothenburg March 31 at 7PM. Please help advertise!

Poster (pdf)
Folder/flyer (pdf)

Co-organizers:

DFD - Banner

Document Freedom Day - Liberate your documents!

String manipulation in bash

Wednesday, February 17th, 2010

In the last post I discussed a script that given a URL to a tarball with a name in a given form, translates the URL to a directory and a filename as follows:

http://ftp.drupal.org/files/projects/nodewords-6.x-1.11.tar.gz is parsed into nodewords-6.x-1.11.tar.gz(the filename) and nodewords(a directory to be).

The script parsed the URL like this:

MODULE_DIR=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f | cut -d '-' -f1)
TAR_BALL=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f)

The first line, MODULE_DIR=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f | cut -d '-' -f1) simply translates all “/” to ” ” (spaces), then loops over each word saving every one of them in the loop variable f and finally cuts the last word at each “-” and returns the first word:

1 http://ftp.drupal.org/files/projects/nodewords-6.x-1.11.tar.gz -> http:  ftp.drupal.org files projects nodewords-6.x-1.11.tar.gz
2 Loop through each word landing on nodewords-6.x-1.11.tar.gz
3 Cut nodewords-6.x-1.11.tar.gz on "-" -> nodewords 6.x 1.11.tar.gz (but return only the first, nodewords )

The second line, TAR_BALL=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f) does the same thing but is only interested in the word after the last “/” which is nodewords 6.x 1.11.tar.gz.

Now this may seem like a lengthy way of splitting and parsing out a substring from a piece of text. So is there any other ways of achieving the same thing without strange loops and stuff?

Luckily, bash has a few ways of manipulating strings built into it. I’ll show a few here.

1. Using the expr(1) command.

expr is a neat little command for evaluating – you guessed it – expressions. It has a match argument which allows you to send in a regepx and parse out substrings in a detailed way.

Consider:

URL="http://ftp.drupal.org/files/projects/nodewords-6.x-1.11.tar.gz"
TARBALL=`expr match "$URL" '.*/\(.*$\)'`

What would we get and how?

The regexp given to expr match is '.*/\(.*$\)'. This means basically discard everything to a “/” that is followed by the substring that ends the line. Which is to say: Return the substring that follows the last “/”.

The escaped parentheses tells expr match that we are interested in a substring, and whatever regexp comes before the first parenthesis tells us where the substring should follow. A simpler example using expr match for substrings will follow.

2. Bash built-in string manipulation tricks

Now consider:

URL="http://ftp.drupal.org/files/projects/nodewords-6.x-1.11.tar.gz"
TARBALL=`expr match "$URL" '.*/\(.*$\)'`
(same as before)

echo ${TARBALL%-*gz}

While we in the last example relied on the command expr, we’re now interested in bash’s built-in feature for stripping the [shortest] match of a substring from the end of a string: ${string%substring} (where string is a variable).

So, echo ${TARBALL%-*gz} (where TARBALL contains nodewords-6.x-1.11.tar.gz ) would give us what we want for directory name, nodewords. This is how it works: Look insided the string nodewords-6.x-1.11.tar.gz and drop everything starting with “-” through and including “gz”.

Of course, you could also use the flavor that strips the longest match from the end, which looks like this: ${string%%substring} (two “%” rather than one).

Then you could say this in order to get the “nodewords” part from the TARBALL: echo ${TARBALL%%-*z} since the longest match from “-” to “z” in nodewords-6.x-1.11.tar.gz is from the first occurrence of “-” to the end of the string.

You could of course also use the counterpart of ${string%%substring} which is ${string##substring} and means “strip the longest match of a substring from the start of a string”. That would allow for the following statement for extracting the tarball from the URL: echo ${URL##http*/} (drop the longext match from and including http through “/” from the front of the URL).

So now we could shorten the script part for extracting “nodewords” and “nodewords-6.x-1.11.tar.gz” from the URL significantly:

TAR_BALL=${URL##http*/}
MODULE_DIR=${TAR_BALL%%-*z}

Some simpler examples

Using expr match for substrings

Consider the string FILE="Flower-30x25.jpg". Let’s say we want to extract the 30×25 part and we know the string will look like Name-NNxNN.jpg. The we would want to find the substring between “-” and the “.” . This is what it looks like:

SIZE=`expr match "$FILE" '.*-\(.*\)\.'`

So, here, the regexp says ignore everything up to and including “-”, give me the substring of everything up to the dot. In a regexp, the “.” has a special meaning of “any character” and the “*” means “zero or many”. If you want to reference an actual dot, you need to escape it: “\.”.

If you didn’t know the size substring would follow a “-”, you’d have to tell expr exactly what it looks like. That could look like this:

SIZE=`expr match "$FILE" '.*\([0-9][0-9]x[0-9][0-9]\)'`

Here you are saying: Skip everything but match the substring that consists of two digits, an “x”, and then two digits again.

The parentheses are escaped with backslash because bash has special meanings of parenthesis which we want to suppress so that they are sent verbatim to the regexp.

Feel free to comment or to leave suggestions!

First!

Monday, February 15th, 2010

So, Gnutiken has finally come around to open its Drupal-based webshop!

It took a little longer than we expected to adopt and tame Drupal to our likings, and some hundred Drupal module installs later, we’re more or less there.

Talking about the Drupal module installations, I got kind of fed up performing the same ritual over and over; download tarball, unpack, change ownership of the newly created directory tree to www-data:www-data, mv the original tarball to a safe place, etc, so I created a small script to do the job:

#!/bin/bash

# Copyright (c) 2010 Rikard Fröberg .
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

usage(){
 echo "USAGE: `basename $0` [-v] url"
 exit 5;
}
bailout(){
 echo $1
 exit $2
}

MINARGS=1

# Sufficient no. of args?
if [ $# -lt $MINARGS ]
then
 usage
fi

if [ "$UID" != "0" ]
then
 bailout "This script needs to be run as root or sudo." 6
fi

WORKING_DIR=/the-path-to-drupal/sites/all/modules
SAVE_DIR=/the-path-to-save-dir/drupal_modules
VERBOSE=false

if [ ! -e $WORKING_DIR ]
then
 bailout "Drupal modules directory NOT found!" 7
fi

while getopts v OPTION
do
 case "$OPTION" in
  v) VERBOSE=true;;
 esac
done
# reset $1 to first argument if option was given
shift $(echo "$OPTIND - 1"|bc)

# Parse arguments
URL=$1
MODULE_DIR=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f | cut -d '-' -f1)
TAR_BALL=$(for f in $(echo $URL|tr '/' ' ');do true;done;echo $f)

# Make sure your are in the right place
$VERBOSE && echo changing directory to: $WORKING_DIR
cd $WORKING_DIR

# Download
$VERBOSE && echo fetching $URL
wget $URL || bailout "Couldn't get file, exiting" 1

# Extract
$VERBOSE && echo extracting files from $TAR_BALL
tar xvzf $TAR_BALL || bailout "couldn't extract $TAR_BALL. Exiting." 2

# Fix ownership
$VERBOSE && echo changing ownership for $WORKING_DIR/$MODULE_DIR to www-data:www-data
chown -R www-data: $WORKING_DIR/$MODULE_DIR || bailout "Couldn't change ownership on ${WORKING_DIR/$MODULE_DIR}. Exiting." 3

# Move the tar ball
$VERBOSE && echo Cleaning up. Moving $TAR_BALL to $SAVE_DIR
mv $TAR_BALL $SAVE_DIR || bailout "Couldn't save $TAR_BALL to $SAVE_DIR" 4

bailout "Done!" 0

How does it work? It uses the fact that Drupal modules are named according to a convention. For instance, the Nodewords module is found here: http://ftp.drupal.org/files/projects/nodewords-6.x-1.11.tar.gz

The tarball extracts to “nodewords” (i.e. the word before the dash in the filename). So the script figures out the filename of the tarball and also the directory (to be chowned later on) by simply looking at the URL.

The script is to be run sudo or as root and takes the URL to the module tarball as the only argument. Of course, you still need to enable the module in Drupal’s administration tool (i.e. /admin/build/modules ) and run update.php as usual, so take it for what it’s worth ;-) .

Hope someone will find any use for it!

If you find any errors or ways to improve it, feel free to leave a comment or publish your own version of it under the same License.

Next I will bring up some alternative ways to parse the URL. Stay tuned.