Skip to content

Instantly share code, notes, and snippets.

@fosskers
Created September 26, 2012 12:39
Show Gist options
  • Save fosskers/3787785 to your computer and use it in GitHub Desktop.
Save fosskers/3787785 to your computer and use it in GitHub Desktop.
Arch Packages

Arch Packages

and what you should know about them

The Arch Way states:

Arch Linux targets and accommodates competent GNU/Linux users by
giving them complete control and *responsibility* over the system.

Part of this comes from package choice. Users either install from established repositories with pacman, or manually from the AUR. All Arch users are familiar with one or both of these processes.

Installing from repos like [core] or [community] means downloading a compressed, pre-built executable, opening it up, and moving it somewhere like /usr/bin. No Windows Installation Wizards here.

See for yourself:

mkdir ~/pkgfun
cd ~/pkgfun
cp /var/cache/pacman/pkg/curl-7.27.0-1-x86_64.pkg.tar.xz ~/pkgfun
xz -d curl-7.27.0-1-x86_64.pkg.tar.xz
tar xf curl-7.27.0-1-x86_64.pkg.tar
cd usr/bin

Hey look, executables.

Installing from the AUR means downloading a source tarball, opening it up, running makepkg and then pacman -U on the result. Not rocket surgery either. Our friend the PKGBUILD makes this easy.

PKGBUILDs

PKGBUILDs help build both official packages and ones from the AUR. Ones in the AUR can be submitted by anybody.

Here's the PKGBUILD from a fun game.

pkgname=dwarffortress-ironhand
pkgver=0.73
_pkgver=34_11
pkgrel=4
pkgdesc="A single-player fantasy game. You control a dwarven outpost or an
adventurer in a randomly generated persistent world. Packed with Ironhand's
tileset and graphics pack.  Does not replace other dwarffortress packages."
arch=(i686 x86_64)
url="http://www.bay12forums.com/smf/index.php?topic=53180.0"
install="dwarffortress-ih.install"
license=('custom:dwarffortress-ih')
depends=(gtk2 mesa sdl_image libsndfile openal sdl_ttf)
makedepends=(unrar)
if [[ $CARCH == 'x86_64' ]]; then
  depends=(lib32-gtk2 lib32-mesa lib32-sdl_image lib32-libsndfile lib32-openal
  lib32-libxdamage lib32-ncurses lib32-sdl_ttf)
  optdepends=('lib32-nvidia-utils: If you have nvidia graphics'
              'lib32-catalyst-utils: If you have ATI graphics'
              'lib32-alsa-plugins: Sound in some setups'
              'lib32-libpulse: Sound in some setups')
fi
source=(http://www.bay12games.com/dwarves/df_${_pkgver}_linux.tar.bz2
        ironhand-${pkgver}.zip::"http://dffd.wimbli.com/download.php?id=6320&f\
=Ironhand+upgrade+${pkgver}.zip"
        dwarffortress-ih dwarffortress-ih.desktop dwarffortress-ih.png)
backup=(opt/df_linux-ih/data/init/init.txt)

I left out the rest. makepkg uses this to find out things about a package. Things like dependencies:

depends=(gtk2 mesa sdl_image libsndfile openal sdl_ttf)

Or even deps based on architecture:

if [[ $CARCH == 'x86_64' ]]; then
  depends=(lib32-gtk2 lib32-mesa lib32-sdl_image lib32-libsndfile lib32-openal
  lib32-libxdamage lib32-ncurses lib32-sdl_ttf)
  optdepends=('lib32-nvidia-utils: If you have nvidia graphics'
              'lib32-catalyst-utils: If you have ATI graphics'
              'lib32-alsa-plugins: Sound in some setups'
              'lib32-libpulse: Sound in some setups')
fi

It even determines source URLs on the fly:

source=(http://www.bay12games.com/dwarves/df_${_pkgver}_linux.tar.bz2)

makepkg itself is a bash script, and being bash it just executes PKGBUILDs to bring their variables into scope like so:

BUILDFILE=${BUILDFILE:-$BUILDSCRIPT}
if [[ ! -f $BUILDFILE ]]; then
        if [[ -t 0 ]]; then
                error "$(gettext "%s does not exist.")" "$BUILDFILE"
                exit 1
        else
                # PKGBUILD passed through a pipe
                BUILDFILE=/dev/stdin
                shopt -u extglob
                source "$BUILDFILE"
                shopt -s extglob
        fi
else

The line source "$BUILDFILE" is the key. A line near the top sets BUILDSCRIPT to equal 'PKGBUILD', so it's clear what all this does.

Let's look at the AUR page of dwarffortress-ironhand.

Hey, version numbers and a dependency list! Anybody who's ever submitted a package to the AUR knows you don't enter those fields manually anywhere. In fact, the AUR backend just takes the submitted PKGBUILD, and... you guessed it! Parses it manually with a custom PHP script. Wait... you didn't guess that, did you?

Rogue Scripts

Try this:

wget -q "https://aur.archlinux.org/packages/li/libpng12/libpng12.tar.gz"
tar xzf libpng12.tar.gz
cd libpng12
sudo makepkg

Did you get this?

colin@ko-linux ~/c/libpng12> sudo makepkg
[sudo] password for colin: 
==> ERROR: Running makepkg as root is a BAD idea and can cause permanent,
catastrophic damage to your system. If you wish to run as root, please
use the --asroot option.

A PKGBUILD is just bash, and makepkg executes it to get its variables.

Let's put a surprise in one and see what happens.

touch secretPasswordFile
echo "rm secretPasswordFile" >> PKGBUILD
makepkg
ls

secretPasswordFile isn't there, is it? We aren't limited to just deleting files, of course. On a server, PKGBUILDs with rogue code could cause a lot of problems. Hence, the Arch folks have the sense to parse each PKGBUILD manually.

As a user, so long as we don't run sudo makepkg --asroot willy-nilly we should be okay. Should be, anyway.

AUR Helpers

Package managers that help build AUR packages are commonly called AUR Helpers. You've heard of a few. They check dependencies and call makepkg for you and everything.

Here's a few lines from one of them:

# Scrapes the aur deps from PKGBUILDS and puts in globally available dependencies array
scrapeaurdeps() {
  pkginfo "$1" "$preview"
  . "$tmpdir/$1.PKGBUILD"
  IFS=$'\n'
  dependencies=( $(echo -e "${depends[*]}\n${makedepends[*]}" | sed -e 's/=.*//' -e 's/>.*//' -e 's/<.*//'| sort -u) )
  unset IFS
}

The . operator is the same as the source keyword. The PKGBUILD is being executed here.

How about code from an A.H. you've probably actually used:

# Read PKGBUILD
# PKGBUILD must be in current directory
# Usage:	read_pkgbuild ($update)
#	$update: 1: call devel_check & devel_update from makepkg
# Set PKGBUILD_VARS, exec "eval $PKGBUILD_VARS" to have PKGBUILD content.
read_pkgbuild() {
	local update=${1:-0}
	local vars=(pkgbase pkgname pkgver pkgrel arch pkgdesc provides url \
		groups license source install md5sums depends makedepends conflicts \
		replaces \
		_svntrunk _svnmod _cvsroot_cvsmod _hgroot _hgrepo \
		_darcsmod _darcstrunk _bzrtrunk _bzrmod _gitroot _gitname \
		)

	unset ${vars[*]}
	local ${vars[*]}
	local pkgbuild_tmp=$(mktemp --tmpdir=".")
	echo "yaourt_$$() {"                            > $pkgbuild_tmp
	cat PKGBUILD                                    >> $pkgbuild_tmp
	echo                                            >> $pkgbuild_tmp
	if (( update )); then
		echo "devel_check"                          >> $pkgbuild_tmp
		# HOLDVER=1 to disable the double check when
		# devel_update() source PKGBUILD
		echo "HOLDVER=1 devel_update"               >> $pkgbuild_tmp
	fi
	echo "declare -p ${vars[*]} 2>/dev/null >&3"    >> $pkgbuild_tmp
	echo "return 0"                                 >> $pkgbuild_tmp
	echo "}"                                        >> $pkgbuild_tmp
	echo "( yaourt_$$ ) || exit 1"                  >> $pkgbuild_tmp
	echo "exit 0"                                   >> $pkgbuild_tmp
	PKGBUILD_VARS="$(makepkg "${MAKEPKG_ARG[@]}" -p "$pkgbuild_tmp" 3>&1 1>/dev/null | tr '\n' ';')"
	rm "$pkgbuild_tmp"
	eval $PKGBUILD_VARS
	pkgbase=${pkgbase:-${pkgname[0]}}
	PKGBUILD_VARS="$(declare -p ${vars[*]} 2>/dev/null | tr '\n' ';')"
	if [[ ! "$pkgbase" ]]; then
		echo $(gettext 'Unable to read PKGBUILD')
		return 1
	fi
	(( ${#pkgname[@]} > 1 )) && {
		warning $(gettext 'This PKGBUILD describes a splitted package.')
		msg $(gettext 'Specific package options are unknown')
	}
	return 0
}

A bit harder to follow, but it turns out this one executes the PKGBUILD as well.

Punchline: makepkg can't protect you here. If the A.H. script itself is run with sudo, a rogue PKGBUILD with a rm -rf / will destroy your filesystem.

In defense of these package managers, they do prompt you to view PKGBUILDs before building. Doing this is responsible behaviour. But what if you look and don't catch the rogue code anyway? What about the people who don't bother to look in the first place?

I'm sure the creators of these package managers had the best of intentions. And I'm sure they worked very hard. They deserve respect for that. I just didn't like what I saw when I read their source.

So I created.

Aura

Aura is a package manager for Arch Linux. As of Sept. 30, it hit Version 1.0. It:

  • is a drop-in replacement for Pacman.
  • fully supports AUR package installation/upgrading/searching.
  • promotes pre-build package research (aura -Ad and aura -Ap)
  • is usable in multiple languages.
  • outputs less than other package managers.
  • parses PKGBUILDs manually.
  • runs makepkg with normal user privilages even when run with sudo.
  • is written in Haskell.
  • has a funky logo. (Type aura -V)

Give Aura a try. You won't be disappointed.

Aura's AUR Page

Github Repo

Aura's Arch Wiki Page

 __ _ _  _ _ _ __ _ 
/ _` | || | '_/ _` |
\__,_|\_,_|_| \__,_|
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment