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 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?
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.
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 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
andaura -Ap
) - is usable in multiple languages.
- outputs less than other package managers.
- parses PKGBUILDs manually.
- runs
makepkg
with normal user privilages even when run withsudo
. - is written in Haskell.
- has a funky logo. (Type
aura -V
)
Give Aura a try. You won't be disappointed.
__ _ _ _ _ _ __ _
/ _` | || | '_/ _` |
\__,_|\_,_|_| \__,_|