mirror of
https://github.com/RfidResearchGroup/proxmark3.git
synced 2026-11-15 00:49:10 +00:00
configure_environment.sh only modelled the USB serial link, so on a PM5 driven over wifi or BLE it printed "plug the Proxmark3 in" and checked nothing that applied. The udev rule and dialout membership stay flagged exactly as before, since every Proxmark gets flashed over USB sooner or later; what was missing was everything else: - the bluetooth group, which 'make accessrights' already adds when the group exists, and which 'pm3 -p bt:' / 'ble:' need. A warning on PM5 or BTADDON, a note elsewhere so a plain PM3 build stays quiet. - cap_net_raw on an installed client. ble_resolve_name() opens a raw HCI socket, so without it 'pm3 -p ble:<name>' needs sudo while 'ble:<MAC>' does not; client/Makefile grants it on install unless SKIP_SETCAP=1. - an absent /dev/ttyACM* no longer reads as an absent Proxmark. Adds client/requirements.txt, since nothing listed the packages the scripts in client/ import: bleak for the BLE roles of pm5_ble_bridge.py (lazy import, the native ble:<MAC> and tcp: transports and the --relay role do not need it), and Unidecode and jq for the two maintenance scrapers. The bleak floor is 0.19 because the API the bridge uses is present in 0.19.5, 0.22.3 and 3.1.0 alike. --extras now checks it too. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1264 lines
43 KiB
Bash
Executable File
1264 lines
43 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
#-----------------------------------------------------------------------------
|
|
# Copyright (C) Proxmark3 contributors. See AUTHORS.md for details.
|
|
#
|
|
# 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 3 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.
|
|
#
|
|
# See LICENSE.txt for the text of the license.
|
|
#-----------------------------------------------------------------------------
|
|
# First-run environment check for a freshly cloned repo: host compilers, ARM
|
|
# toolchain, client libraries, Python venv + requirements, device access.
|
|
#
|
|
# Reports by default and changes nothing, except that it offers to create a
|
|
# Python venv when no venv is found at all. Pass --install to let it install
|
|
# the missing packages too.
|
|
#-----------------------------------------------------------------------------
|
|
|
|
set -u
|
|
|
|
PM3PATH="$(cd "$(dirname "$0")" && pwd)"
|
|
cd "$PM3PATH" || exit 1
|
|
|
|
LANG=C
|
|
|
|
# --- options -----------------------------------------------------------------
|
|
|
|
DO_INSTALL=false
|
|
ASSUME_YES=false
|
|
WANT_EXTRAS=false
|
|
USE_COLOR=auto
|
|
VENV_OPT=""
|
|
NO_VENV=false
|
|
|
|
usage() {
|
|
cat <<USAGE
|
|
Usage: ./configure_environment.sh [options]
|
|
|
|
Checks that this machine can build the Proxmark3 client and firmware, that the
|
|
chosen PLATFORM matches your device, and that the Python side (venv +
|
|
requirements) is in place. Run it once after cloning.
|
|
|
|
'make configure' runs the same thing. Call this script directly when make itself
|
|
refuses to start, e.g. after a typo in PLATFORM, since make stops on that before
|
|
it could run any target.
|
|
|
|
Options:
|
|
--install install what is missing (system packages via the detected
|
|
package manager, venv, Python requirements). Asks first.
|
|
-y, --yes don't ask, assume yes. Only meaningful with --install.
|
|
--extras also consider the optional requirement files
|
|
(fpga/, tools/ePassport/, tools/btaddon/).
|
|
--venv PATH use this venv instead of searching for one.
|
|
--no-venv don't look for or offer a venv, check the system python.
|
|
--no-color plain output.
|
|
-h, --help this text.
|
|
|
|
Environment:
|
|
PM3_VENV same as --venv.
|
|
CROSS ARM toolchain prefix, default arm-none-eabi-
|
|
CC host C compiler used for the library probes, default gcc
|
|
|
|
Exit status: 0 when everything required is present, 1 when something required
|
|
is missing, 2 on a usage error.
|
|
USAGE
|
|
}
|
|
|
|
while [ $# -gt 0 ]; do
|
|
case "$1" in
|
|
--install) DO_INSTALL=true ;;
|
|
-y|--yes) ASSUME_YES=true ;;
|
|
--extras) WANT_EXTRAS=true ;;
|
|
--no-color) USE_COLOR=false ;;
|
|
--no-venv) NO_VENV=true ;;
|
|
--venv)
|
|
shift
|
|
[ $# -gt 0 ] || { echo "--venv needs a path" >&2; exit 2; }
|
|
VENV_OPT="$1"
|
|
;;
|
|
--venv=*) VENV_OPT="${1#--venv=}" ;;
|
|
-h|--help) usage; exit 0 ;;
|
|
*)
|
|
echo "unknown option: $1" >&2
|
|
echo "try: ./configure_environment.sh --help" >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
shift
|
|
done
|
|
|
|
[ -n "$VENV_OPT" ] || VENV_OPT="${PM3_VENV:-}"
|
|
|
|
# --- output ------------------------------------------------------------------
|
|
|
|
if [ "$USE_COLOR" = "auto" ]; then
|
|
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-dumb}" != "dumb" ]; then
|
|
USE_COLOR=true
|
|
else
|
|
USE_COLOR=false
|
|
fi
|
|
fi
|
|
|
|
if [ "$USE_COLOR" = "true" ]; then
|
|
C_RST=$'\033[0m'
|
|
C_RED=$'\033[31m'
|
|
C_GRN=$'\033[32m'
|
|
C_YEL=$'\033[33m'
|
|
C_CYA=$'\033[36m'
|
|
C_BLD=$'\033[1m'
|
|
else
|
|
C_RST=""; C_RED=""; C_GRN=""; C_YEL=""; C_CYA=""; C_BLD=""
|
|
fi
|
|
|
|
T_OK="[ ${C_GRN}ok${C_RST} ]"
|
|
T_FAIL="[ ${C_RED}fail${C_RST} ]"
|
|
T_WARN="[ ${C_YEL}warn${C_RST} ]"
|
|
T_SKIP="[ skip ]"
|
|
T_INFO="[ ${C_CYA}info${C_RST} ]"
|
|
|
|
N_OK=0
|
|
N_FAIL=0
|
|
N_WARN=0
|
|
|
|
MISSING_REQ=""
|
|
MISSING_OPT=""
|
|
NEED_ACCESSRIGHTS=false
|
|
NEED_UDEV=false
|
|
DEV_GROUP=""
|
|
|
|
section() { printf '\n%s%s%s\n' "$C_BLD" "$1" "$C_RST"; }
|
|
res_ok() { printf '%s %s\n' "$T_OK" "$1"; N_OK=$((N_OK + 1)); }
|
|
res_warn() { printf '%s %s\n' "$T_WARN" "$1"; N_WARN=$((N_WARN + 1)); }
|
|
res_skip() { printf '%s %s\n' "$T_SKIP" "$1"; }
|
|
res_info() { printf '%s %s\n' "$T_INFO" "$1"; }
|
|
res_hint() { printf ' %s\n' "$1"; }
|
|
|
|
res_fail() {
|
|
printf '%s %s\n' "$T_FAIL" "$1"
|
|
N_FAIL=$((N_FAIL + 1))
|
|
}
|
|
|
|
# miss_req KEY MESSAGE - required, counts as a failure
|
|
miss_req() {
|
|
[ "$1" = "-" ] || MISSING_REQ="$MISSING_REQ $1"
|
|
res_fail "$2"
|
|
}
|
|
|
|
# miss_opt KEY MESSAGE - optional, only disables a feature
|
|
miss_opt() {
|
|
[ "$1" = "-" ] || MISSING_OPT="$MISSING_OPT $1"
|
|
res_warn "$2"
|
|
}
|
|
|
|
# miss_note KEY MESSAGE - off by default anyway, just mention the package
|
|
miss_note() {
|
|
[ "$1" = "-" ] || MISSING_OPT="$MISSING_OPT $1"
|
|
res_skip "$2"
|
|
}
|
|
|
|
# ask QUESTION - true on yes. Never blocks when there is no terminal.
|
|
ask() {
|
|
if [ "$ASSUME_YES" = "true" ]; then
|
|
printf '%s [y/N] y\n' "$1"
|
|
return 0
|
|
fi
|
|
|
|
if [ ! -t 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
local reply=""
|
|
printf '%s [y/N] ' "$1"
|
|
read -r reply || return 1
|
|
|
|
case "$reply" in
|
|
[yY]|[yY][eE][sS]) return 0 ;;
|
|
*) return 1 ;;
|
|
esac
|
|
}
|
|
|
|
have() { command -v "$1" >/dev/null 2>&1; }
|
|
|
|
first_line() { "$@" 2>/dev/null | head -n 1; }
|
|
|
|
# --- platform detection ------------------------------------------------------
|
|
|
|
OS="$(uname -s)"
|
|
DISTRO=""
|
|
DISTRO_NAME=""
|
|
PKG_MGR=""
|
|
IS_WSL=false
|
|
|
|
case "$OS" in
|
|
Linux)
|
|
if [ -r /proc/version ] && grep -qi microsoft /proc/version 2>/dev/null; then
|
|
IS_WSL=true
|
|
fi
|
|
|
|
if [ -r /etc/os-release ]; then
|
|
DISTRO="$(. /etc/os-release 2>/dev/null; echo "${ID:-}")"
|
|
DISTRO_NAME="$(. /etc/os-release 2>/dev/null; echo "${PRETTY_NAME:-$ID}")"
|
|
DISTRO_LIKE="$(. /etc/os-release 2>/dev/null; echo "${ID_LIKE:-}")"
|
|
else
|
|
DISTRO_LIKE=""
|
|
fi
|
|
|
|
case " $DISTRO $DISTRO_LIKE " in
|
|
*" nixos "*) PKG_MGR="nix" ;;
|
|
*" debian "*|*" ubuntu "*) PKG_MGR="apt" ;;
|
|
*" arch "*|*" archlinux "*) PKG_MGR="pacman" ;;
|
|
*" fedora "*|*" rhel "*|*" centos "*) PKG_MGR="dnf" ;;
|
|
*" suse "*|*" opensuse "*) PKG_MGR="zypper" ;;
|
|
esac
|
|
|
|
# fall back on whatever is actually installed
|
|
if [ -z "$PKG_MGR" ]; then
|
|
for m in apt-get pacman dnf zypper; do
|
|
if have "$m"; then
|
|
case "$m" in
|
|
apt-get) PKG_MGR="apt" ;;
|
|
*) PKG_MGR="$m" ;;
|
|
esac
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
;;
|
|
Darwin)
|
|
DISTRO="macos"
|
|
DISTRO_NAME="macOS $(sw_vers -productVersion 2>/dev/null)"
|
|
if have brew; then
|
|
PKG_MGR="brew"
|
|
elif have port; then
|
|
PKG_MGR="port"
|
|
fi
|
|
;;
|
|
MINGW*|MSYS*|CYGWIN*)
|
|
DISTRO="windows"
|
|
DISTRO_NAME="$OS (ProxSpace / MSYS)"
|
|
;;
|
|
*)
|
|
DISTRO_NAME="$OS"
|
|
;;
|
|
esac
|
|
|
|
[ -n "$DISTRO_NAME" ] || DISTRO_NAME="$OS"
|
|
[ "$IS_WSL" = "true" ] && DISTRO_NAME="$DISTRO_NAME (WSL)"
|
|
|
|
# pkg_for KEY - package name for the detected package manager, empty if none
|
|
pkg_for() {
|
|
case "$PKG_MGR" in
|
|
apt)
|
|
case "$1" in
|
|
cc|cxx|make) echo "build-essential" ;;
|
|
pkgconfig) echo "pkg-config" ;;
|
|
git) echo "git ca-certificates" ;;
|
|
cmake) echo "cmake" ;;
|
|
clang) echo "clang" ;;
|
|
armgcc) echo "gcc-arm-none-eabi" ;;
|
|
armlibc) echo "libnewlib-dev" ;;
|
|
readline) echo "libreadline-dev" ;;
|
|
bzip2) echo "libbz2-dev" ;;
|
|
lz4) echo "liblz4-dev" ;;
|
|
openssl) echo "libssl-dev" ;;
|
|
zlib) echo "zlib1g-dev" ;;
|
|
bluez) echo "libbluetooth-dev" ;;
|
|
pythondev) echo "libpython3-dev" ;;
|
|
qt) echo "qt6-base-dev" ;;
|
|
gd) echo "libgd-dev" ;;
|
|
opencl) echo "ocl-icd-opencl-dev" ;;
|
|
python3) echo "python3" ;;
|
|
venv) echo "python3-venv" ;;
|
|
pip) echo "python3-pip" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
pacman)
|
|
case "$1" in
|
|
cc|cxx|make) echo "base-devel" ;;
|
|
pkgconfig) echo "pkgconf" ;;
|
|
git) echo "git" ;;
|
|
cmake) echo "cmake" ;;
|
|
clang) echo "clang" ;;
|
|
armgcc) echo "arm-none-eabi-gcc" ;;
|
|
armlibc) echo "arm-none-eabi-newlib" ;;
|
|
readline) echo "readline" ;;
|
|
bzip2) echo "bzip2" ;;
|
|
lz4) echo "lz4" ;;
|
|
openssl) echo "openssl" ;;
|
|
zlib) echo "zlib" ;;
|
|
bluez) echo "bluez bluez-libs" ;;
|
|
pythondev) echo "python" ;;
|
|
qt) echo "qt6-base" ;;
|
|
gd) echo "gd" ;;
|
|
opencl) echo "ocl-icd" ;;
|
|
python3) echo "python" ;;
|
|
pip) echo "python-pip" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
dnf)
|
|
case "$1" in
|
|
cc) echo "gcc" ;;
|
|
cxx) echo "gcc-c++" ;;
|
|
make) echo "make" ;;
|
|
pkgconfig) echo "pkgconf-pkg-config" ;;
|
|
git) echo "git" ;;
|
|
cmake) echo "cmake" ;;
|
|
clang) echo "clang" ;;
|
|
armgcc) echo "arm-none-eabi-gcc-cs" ;;
|
|
armlibc) echo "arm-none-eabi-newlib" ;;
|
|
readline) echo "readline-devel" ;;
|
|
bzip2) echo "bzip2-devel" ;;
|
|
lz4) echo "lz4-devel" ;;
|
|
openssl) echo "openssl-devel" ;;
|
|
zlib) echo "zlib-ng-compat-devel" ;;
|
|
bluez) echo "bluez-libs-devel" ;;
|
|
pythondev) echo "python3-devel" ;;
|
|
qt) echo "qt6-qtbase-devel" ;;
|
|
gd) echo "gd-devel" ;;
|
|
opencl) echo "mesa-libOpenCL ocl-icd-devel" ;;
|
|
python3) echo "python3" ;;
|
|
pip) echo "python3-pip" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
zypper)
|
|
case "$1" in
|
|
cc|cxx) echo "gcc-c++" ;;
|
|
make) echo "patterns-devel-base-devel_basis" ;;
|
|
pkgconfig) echo "pkg-config" ;;
|
|
git) echo "git" ;;
|
|
cmake) echo "cmake" ;;
|
|
clang) echo "clang" ;;
|
|
armgcc) echo "cross-arm-none-gcc12" ;;
|
|
armlibc) echo "cross-arm-none-newlib-devel" ;;
|
|
readline) echo "readline-devel" ;;
|
|
bzip2) echo "libbz2-devel" ;;
|
|
lz4) echo "liblz4-devel" ;;
|
|
openssl) echo "libopenssl-devel" ;;
|
|
zlib) echo "zlib-devel" ;;
|
|
bluez) echo "bluez-devel" ;;
|
|
pythondev) echo "python3-devel" ;;
|
|
qt) echo "qt6-core-devel qt6-widgets-devel" ;;
|
|
gd) echo "gd-devel" ;;
|
|
opencl) echo "ocl-icd-devel" ;;
|
|
python3) echo "python3" ;;
|
|
pip) echo "python3-pip" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
brew)
|
|
case "$1" in
|
|
pkgconfig) echo "pkgconf" ;;
|
|
git) echo "git" ;;
|
|
cmake) echo "cmake" ;;
|
|
armgcc) echo "RfidResearchGroup/proxmark3/arm-none-eabi-gcc" ;;
|
|
readline) echo "readline" ;;
|
|
lz4) echo "lz4" ;;
|
|
openssl) echo "openssl" ;;
|
|
zlib) echo "zlib" ;;
|
|
qt) echo "qt" ;;
|
|
gd) echo "gd" ;;
|
|
python3) echo "python3" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
port)
|
|
case "$1" in
|
|
pkgconfig) echo "pkgconfig" ;;
|
|
git) echo "git" ;;
|
|
cmake) echo "cmake" ;;
|
|
armgcc) echo "arm-none-eabi-gcc arm-none-eabi-binutils" ;;
|
|
readline) echo "readline" ;;
|
|
bzip2) echo "bzip2" ;;
|
|
lz4) echo "lz4" ;;
|
|
openssl) echo "openssl11" ;;
|
|
zlib) echo "zlib" ;;
|
|
qt) echo "qt6 qt6-qtbase" ;;
|
|
gd) echo "gd2" ;;
|
|
python3) echo "python312" ;;
|
|
astyle) echo "astyle" ;;
|
|
recode) echo "recode" ;;
|
|
esac ;;
|
|
esac
|
|
}
|
|
|
|
pkg_install_cmd() {
|
|
case "$PKG_MGR" in
|
|
apt) echo "sudo apt-get install --no-install-recommends $*" ;;
|
|
pacman) echo "sudo pacman -Syu --needed $*" ;;
|
|
dnf) echo "sudo dnf install $*" ;;
|
|
zypper) echo "sudo zypper install $*" ;;
|
|
brew) echo "brew install $*" ;;
|
|
port) echo "sudo port install $*" ;;
|
|
nix) echo "nix-shell tools/shell.nix" ;;
|
|
*) echo "" ;;
|
|
esac
|
|
}
|
|
|
|
# resolve keys to package names, dropping the ones this platform has no name for
|
|
pkgs_for_keys() {
|
|
local key out=""
|
|
for key in $(echo "$*" | tr ' ' '\n' | sort -u); do
|
|
[ -n "$key" ] || continue
|
|
local p
|
|
p="$(pkg_for "$key")"
|
|
[ -n "$p" ] && out="$out $p"
|
|
done
|
|
echo "$out" | tr ' ' '\n' | sort -u | tr '\n' ' ' | sed 's/^ *//; s/ *$//'
|
|
}
|
|
|
|
# --- library probes ----------------------------------------------------------
|
|
|
|
PROBE_DIR=""
|
|
cleanup() { [ -n "$PROBE_DIR" ] && rm -rf "$PROBE_DIR"; }
|
|
trap cleanup EXIT HUP INT TERM
|
|
|
|
CC="${CC:-gcc}"
|
|
have "$CC" || CC="cc"
|
|
have "$CC" || CC="clang"
|
|
|
|
# cc_probe HEADER [LDFLAGS...] - can we compile and link against it?
|
|
cc_probe() {
|
|
have "$CC" || return 2
|
|
|
|
if [ -z "$PROBE_DIR" ]; then
|
|
PROBE_DIR="$(mktemp -d 2>/dev/null)" || return 2
|
|
fi
|
|
|
|
local hdr="$1"
|
|
shift
|
|
printf '#include <%s>\nint main(void) { return 0; }\n' "$hdr" > "$PROBE_DIR/probe.c"
|
|
"$CC" "$PROBE_DIR/probe.c" -o "$PROBE_DIR/probe.out" "$@" >/dev/null 2>&1
|
|
}
|
|
|
|
pc_exists() { have pkg-config && pkg-config --exists "$@" 2>/dev/null; }
|
|
pc_ver() { pkg-config --modversion "$1" 2>/dev/null | head -n 1; }
|
|
|
|
# check_cmd KEY COMMAND LABEL REQUIRED
|
|
check_cmd() {
|
|
local key="$1" cmd="$2" label="$3" required="$4"
|
|
|
|
if have "$cmd"; then
|
|
local v
|
|
v="$(first_line "$cmd" --version | sed 's/^[^0-9]*\([0-9][0-9.]*\).*/\1/')"
|
|
if [ -n "$v" ]; then
|
|
res_ok "$label - $cmd $v"
|
|
else
|
|
res_ok "$label - $cmd"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
case "$required" in
|
|
req) miss_req "$key" "$label - '$cmd' not found" ;;
|
|
note) miss_note "$key" "$label - '$cmd' not found" ;;
|
|
*) miss_opt "$key" "$label - '$cmd' not found" ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
# check_lib KEY LABEL REQUIRED PCNAME HEADER [LDFLAGS...]
|
|
check_lib() {
|
|
local key="$1" label="$2" required="$3" pcname="$4" hdr="$5"
|
|
shift 5
|
|
|
|
if [ -n "$pcname" ] && pc_exists "$pcname"; then
|
|
res_ok "$label - $(pc_ver "$pcname")"
|
|
return 0
|
|
fi
|
|
|
|
if [ -n "$hdr" ] && cc_probe "$hdr" "$@"; then
|
|
res_ok "$label"
|
|
return 0
|
|
fi
|
|
|
|
local why="not found"
|
|
if [ -n "$hdr" ] && ! have "$CC"; then
|
|
why="not found (no C compiler to probe with)"
|
|
fi
|
|
|
|
case "$required" in
|
|
req) miss_req "$key" "$label - $why" ;;
|
|
note) miss_note "$key" "$label - $why" ;;
|
|
*) miss_opt "$key" "$label - $why" ;;
|
|
esac
|
|
return 1
|
|
}
|
|
|
|
#-----------------------------------------------------------------------------
|
|
printf '%s\n' "${C_BLD}Proxmark3 environment check${C_RST}"
|
|
printf '%s\n' " repo : $PM3PATH"
|
|
printf '%s\n' " system : $DISTRO_NAME"
|
|
printf '%s\n' " packages : ${PKG_MGR:-unknown package manager}"
|
|
|
|
# --- 1. repo sanity ----------------------------------------------------------
|
|
|
|
section "Repository"
|
|
|
|
if [ -f "$PM3PATH/Makefile" ] && [ -d "$PM3PATH/armsrc" ] && [ -d "$PM3PATH/client" ]; then
|
|
res_ok "repo layout looks right"
|
|
else
|
|
res_fail "this does not look like a proxmark3 checkout"
|
|
res_hint "run the script from the root of the clone"
|
|
fi
|
|
|
|
if have git && git -C "$PM3PATH" rev-parse --git-dir >/dev/null 2>&1; then
|
|
res_ok "git clone - $(git -C "$PM3PATH" rev-parse --short HEAD 2>/dev/null)"
|
|
else
|
|
res_warn "not a git clone - the client will report an unknown version"
|
|
res_hint "builds work, but 'hw version' cannot show the commit"
|
|
fi
|
|
|
|
# the ARM build has no mkdir for its object dirs, they live in git via .dummy
|
|
DUMMY_MISSING=""
|
|
for d in armsrc/obj/.dummy armsrc/obj/Standalone/.dummy bootrom/obj/.dummy; do
|
|
[ -f "$PM3PATH/$d" ] || DUMMY_MISSING="$DUMMY_MISSING $d"
|
|
done
|
|
|
|
if [ -z "$DUMMY_MISSING" ]; then
|
|
res_ok "firmware object directories present"
|
|
else
|
|
res_fail "missing tracked placeholder(s):$DUMMY_MISSING"
|
|
res_hint "the ARM build cannot create these directories, restore them with:"
|
|
res_hint " git checkout --$DUMMY_MISSING"
|
|
fi
|
|
|
|
|
|
# --- 2. which device --------------------------------------------------------
|
|
|
|
section "Device / platform"
|
|
|
|
PLATFORM_KNOWN="PM3RDV4 PM3GENERIC PM3ICOPYX PM3ULTIMATE PM5"
|
|
EXTRAS_KNOWN="SMARTCARD FLASH BTADDON FPC_USART_DEV BWM NO_LOWBATT_SHUTDOWN NO_LOWBATT_BEEP"
|
|
|
|
# mk_var FILE NAME - value of an uncommented NAME= assignment, last one wins
|
|
mk_var() {
|
|
[ -f "$1" ] || return 0
|
|
sed -n "s/^[[:space:]]*$2[[:space:]]*[:+?]*=[[:space:]]*//p" "$1" \
|
|
| sed 's/[[:space:]]*#.*$//; s/[[:space:]]*$//' \
|
|
| tail -n 1
|
|
}
|
|
|
|
in_list() {
|
|
local needle="$1" item
|
|
for item in $2; do
|
|
[ "$item" = "$needle" ] && return 0
|
|
done
|
|
return 1
|
|
}
|
|
|
|
platform_cheatsheet() {
|
|
res_hint "PM3RDV4 Proxmark3 RDV4 (the default)"
|
|
res_hint "PM3GENERIC Proxmark3 RDV1, RDV2, Easy, rysccorps, clones"
|
|
res_hint "PM3ICOPYX iCopy-X and Proxmark3 Max, with PLATFORM_EXTRAS=FLASH"
|
|
res_hint "PM3ULTIMATE Proxmark3 Ultimate (XC2S50)"
|
|
res_hint "PM5 Proxmark5 Iceman Edition (Artery AT32F435/437)"
|
|
res_hint "full table, including the standalone modes: make PLATFORM="
|
|
}
|
|
|
|
PLATFORM_FILE="$PM3PATH/Makefile.platform"
|
|
PLATFORM_SRC=""
|
|
PLATFORM_VAL=""
|
|
EXTRAS_VAL=""
|
|
STANDALONE_VAL=""
|
|
LED_ORDER_VAL=""
|
|
SKIP_CEP_VAL=""
|
|
|
|
if [ -f "$PLATFORM_FILE" ]; then
|
|
# a plain PLATFORM= in the file beats the environment, same as make sees it
|
|
PLATFORM_VAL="$(mk_var "$PLATFORM_FILE" PLATFORM)"
|
|
EXTRAS_VAL="$(mk_var "$PLATFORM_FILE" PLATFORM_EXTRAS)"
|
|
STANDALONE_VAL="$(mk_var "$PLATFORM_FILE" STANDALONE)"
|
|
LED_ORDER_VAL="$(mk_var "$PLATFORM_FILE" LED_ORDER)"
|
|
SKIP_CEP_VAL="$(mk_var "$PLATFORM_FILE" SKIP_CEP)"
|
|
PLATFORM_SRC="Makefile.platform"
|
|
fi
|
|
|
|
if [ -z "$PLATFORM_VAL" ] && [ -n "${PLATFORM:-}" ]; then
|
|
PLATFORM_VAL="$PLATFORM"
|
|
EXTRAS_VAL="${PLATFORM_EXTRAS:-}"
|
|
PLATFORM_SRC="environment"
|
|
fi
|
|
|
|
if [ -z "$PLATFORM_VAL" ]; then
|
|
PLATFORM_VAL="PM3RDV4"
|
|
PLATFORM_SRC="default"
|
|
fi
|
|
|
|
if in_list "$PLATFORM_VAL" "$PLATFORM_KNOWN"; then
|
|
# with no file at all the warning further down says it better
|
|
if [ "$PLATFORM_SRC" != "default" ]; then
|
|
res_ok "PLATFORM=$PLATFORM_VAL (from $PLATFORM_SRC)"
|
|
fi
|
|
elif [ "$PLATFORM_VAL" = "PM3OTHER" ]; then
|
|
res_warn "PLATFORM=PM3OTHER is deprecated, use PM3GENERIC instead"
|
|
else
|
|
res_fail "PLATFORM=$PLATFORM_VAL is not a known platform, the firmware build will stop"
|
|
platform_cheatsheet
|
|
fi
|
|
|
|
if [ -n "$EXTRAS_VAL" ]; then
|
|
EXTRAS_BAD=""
|
|
for tok in $EXTRAS_VAL; do
|
|
if ! in_list "$tok" "$EXTRAS_KNOWN"; then
|
|
res_fail "PLATFORM_EXTRAS token '$tok' is not recognised"
|
|
EXTRAS_BAD="yes"
|
|
continue
|
|
fi
|
|
|
|
case "$tok" in
|
|
BWM|NO_LOWBATT_SHUTDOWN|NO_LOWBATT_BEEP)
|
|
if [ "$PLATFORM_VAL" != "PM5" ]; then
|
|
res_fail "PLATFORM_EXTRAS=$tok is PM5 only, but PLATFORM=$PLATFORM_VAL"
|
|
EXTRAS_BAD="yes"
|
|
fi
|
|
;;
|
|
BTADDON|FPC_USART_DEV)
|
|
if [ "$PLATFORM_VAL" = "PM5" ]; then
|
|
res_fail "PLATFORM_EXTRAS=$tok is not available on PM5"
|
|
EXTRAS_BAD="yes"
|
|
fi
|
|
;;
|
|
esac
|
|
done
|
|
|
|
[ -z "$EXTRAS_BAD" ] && res_ok "PLATFORM_EXTRAS=$EXTRAS_VAL"
|
|
fi
|
|
|
|
if [ -n "$STANDALONE_VAL" ]; then
|
|
res_info "STANDALONE=$STANDALONE_VAL"
|
|
res_hint "'make PLATFORM=' lists which modes each platform supports"
|
|
fi
|
|
|
|
if [ -n "$LED_ORDER_VAL" ] && [ "$PLATFORM_VAL" != "PM3GENERIC" ]; then
|
|
res_warn "LED_ORDER=$LED_ORDER_VAL only applies to PLATFORM=PM3GENERIC, it is ignored here"
|
|
fi
|
|
|
|
if [ "$SKIP_CEP_VAL" = "1" ] && [ "$PLATFORM_VAL" != "PM5" ]; then
|
|
res_fail "SKIP_CEP=1 is PM5 only, but PLATFORM=$PLATFORM_VAL"
|
|
fi
|
|
|
|
if [ "$PLATFORM_VAL" = "PM5" ]; then
|
|
res_info "PM5 notes: doc/md/PM5_Start_Here/Getting_Started_With_PM5.md"
|
|
fi
|
|
|
|
# no Makefile.platform at all - offer to write one, the firmware is device specific
|
|
if [ ! -f "$PLATFORM_FILE" ]; then
|
|
if [ "$PLATFORM_SRC" = "environment" ]; then
|
|
res_warn "no Makefile.platform, PLATFORM=$PLATFORM_VAL comes from the environment"
|
|
res_hint "it has to be set for every make, put it in Makefile.platform instead"
|
|
else
|
|
res_warn "no Makefile.platform, every build will be for PLATFORM=PM3RDV4"
|
|
res_hint "flashing an RDV4 image to another board gives a non-working device"
|
|
fi
|
|
|
|
platform_cheatsheet
|
|
|
|
if [ -t 0 ] && [ "$ASSUME_YES" != "true" ]; then
|
|
printf '\n Which device do you have?\n'
|
|
printf ' 1) Proxmark3 RDV4\n'
|
|
printf ' 2) Proxmark3 RDV4 with the Blue Shark BT add-on\n'
|
|
printf ' 3) Proxmark3 RDV1 / RDV2 / Easy / generic clone\n'
|
|
printf ' 4) Proxmark5 Iceman Edition\n'
|
|
printf ' 5) Proxmark5 with the Battery Wireless Mod\n'
|
|
printf ' 6) iCopy-X or Proxmark3 Max\n'
|
|
printf ' 7) Proxmark3 Ultimate\n'
|
|
printf ' 0) skip, I will write Makefile.platform myself\n'
|
|
printf ' choice [0]: '
|
|
|
|
PLT_CHOICE=""
|
|
read -r PLT_CHOICE || PLT_CHOICE=""
|
|
|
|
NEW_PLT=""
|
|
NEW_EXTRAS=""
|
|
case "$PLT_CHOICE" in
|
|
1) NEW_PLT="PM3RDV4" ;;
|
|
2) NEW_PLT="PM3RDV4"; NEW_EXTRAS="BTADDON" ;;
|
|
3) NEW_PLT="PM3GENERIC" ;;
|
|
4) NEW_PLT="PM5" ;;
|
|
5) NEW_PLT="PM5"; NEW_EXTRAS="BWM" ;;
|
|
6) NEW_PLT="PM3ICOPYX"; NEW_EXTRAS="FLASH" ;;
|
|
7) NEW_PLT="PM3ULTIMATE" ;;
|
|
*) res_hint "skipped, see Makefile.platform.sample for every option" ;;
|
|
esac
|
|
|
|
if [ -n "$NEW_PLT" ]; then
|
|
{
|
|
echo "# Written by configure_environment.sh. See Makefile.platform.sample"
|
|
echo "# for every available option, or run 'make PLATFORM=' for the full list."
|
|
echo "PLATFORM=$NEW_PLT"
|
|
[ -n "$NEW_EXTRAS" ] && echo "PLATFORM_EXTRAS=$NEW_EXTRAS"
|
|
} > "$PLATFORM_FILE"
|
|
|
|
PLATFORM_VAL="$NEW_PLT"
|
|
EXTRAS_VAL="$NEW_EXTRAS"
|
|
res_ok "wrote Makefile.platform - PLATFORM=$NEW_PLT${NEW_EXTRAS:+ PLATFORM_EXTRAS=$NEW_EXTRAS}"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# a changed platform needs the object files rebuilt, make handles it via this cache
|
|
if [ -f "$PM3PATH/.Makefile.options.cache" ]; then
|
|
CACHED_PLT="$(mk_var "$PM3PATH/.Makefile.options.cache" CACHED_PLATFORM)"
|
|
if [ -n "$CACHED_PLT" ] && [ "$CACHED_PLT" != "$PLATFORM_VAL" ]; then
|
|
res_info "last build was for $CACHED_PLT, make will rebuild what the change affects"
|
|
fi
|
|
fi
|
|
|
|
# --- 3. host toolchain -------------------------------------------------------
|
|
|
|
section "Host toolchain"
|
|
|
|
if have make; then
|
|
if first_line make --version | grep -q GNU; then
|
|
res_ok "make - $(first_line make --version)"
|
|
else
|
|
miss_req "make" "make is not GNU make - $(first_line make --version)"
|
|
res_hint "the build needs GNU make, try gmake or install it"
|
|
fi
|
|
else
|
|
miss_req "make" "make - not found"
|
|
fi
|
|
|
|
check_cmd cc gcc "host C compiler" req
|
|
check_cmd cxx g++ "host C++ compiler" req
|
|
check_cmd pkgconfig pkg-config "pkg-config" req
|
|
check_cmd git git "git" req
|
|
|
|
if have gcc; then
|
|
GCC_MAJOR="$(gcc -dumpfullversion -dumpversion 2>/dev/null | cut -d. -f1)"
|
|
case "$GCC_MAJOR" in
|
|
''|*[!0-9]*) : ;;
|
|
*)
|
|
if [ "$GCC_MAJOR" -lt 8 ]; then
|
|
res_warn "gcc $GCC_MAJOR is older than 8 - Qt6 will not build"
|
|
res_hint "build with 'make SKIPQT6=1', or install a newer gcc"
|
|
fi
|
|
;;
|
|
esac
|
|
fi
|
|
|
|
check_cmd clang clang "second compiler (optional, for extra warnings)" note
|
|
check_cmd cmake cmake "cmake (optional, alternative client build)" note
|
|
|
|
# --- 4. ARM toolchain --------------------------------------------------------
|
|
|
|
section "ARM firmware toolchain"
|
|
|
|
CROSS="${CROSS:-arm-none-eabi-}"
|
|
|
|
if have "${CROSS}gcc"; then
|
|
res_ok "${CROSS}gcc - $(first_line "${CROSS}gcc" -dumpversion)"
|
|
|
|
ARM_LIBC="$("${CROSS}gcc" -print-file-name=libc.a 2>/dev/null)"
|
|
if [ -n "$ARM_LIBC" ] && [ "$ARM_LIBC" != "libc.a" ] && [ -f "$ARM_LIBC" ]; then
|
|
res_ok "ARM C library - $ARM_LIBC"
|
|
else
|
|
miss_req "armlibc" "ARM C library not found - ${CROSS}gcc cannot link"
|
|
res_hint "install newlib, or picolibc where newlib conflicts with the compiler"
|
|
fi
|
|
else
|
|
miss_req "armgcc" "${CROSS}gcc - not found, firmware cannot be built"
|
|
res_hint "the host client alone still builds with 'make client'"
|
|
fi
|
|
|
|
for t in objcopy objdump; do
|
|
if have "${CROSS}${t}"; then
|
|
res_ok "${CROSS}${t}"
|
|
else
|
|
miss_req "armgcc" "${CROSS}${t} - not found"
|
|
fi
|
|
done
|
|
|
|
# --- 5. client libraries -----------------------------------------------------
|
|
|
|
section "Client libraries (required)"
|
|
|
|
check_lib readline "readline" req "" readline/readline.h -lreadline
|
|
check_lib bzip2 "bzip2 (libbz2)" req bzip2 bzlib.h -lbz2
|
|
check_lib lz4 "lz4" req liblz4 lz4.h -llz4
|
|
check_lib openssl "openssl (libcrypto, for mfd_aes_brute/mfulc_des_brute)" \
|
|
req libcrypto openssl/evp.h -lcrypto
|
|
|
|
section "Client libraries (optional, each one enables a feature)"
|
|
|
|
check_lib zlib "zlib - compressed Google Smart Tap payloads" opt zlib zlib.h -lz
|
|
check_lib bluez "bluez - native Bluetooth support" opt bluez "" || \
|
|
res_hint "not available on every distro, the BT add-on also works over a serial port"
|
|
|
|
if pc_exists python3-embed; then
|
|
res_ok "libpython3 - $(pc_ver python3-embed) (embedded Python scripting)"
|
|
elif pc_exists python3; then
|
|
res_ok "libpython3 - $(pc_ver python3) (embedded Python scripting)"
|
|
else
|
|
miss_opt pythondev "libpython3 - not found, Python scripts disabled in the client"
|
|
fi
|
|
|
|
if pc_exists Qt6Core Qt6Widgets Qt6Gui; then
|
|
res_ok "Qt6 - $(pc_ver Qt6Core) (graphs, 'hw tune' plot)"
|
|
elif have qmake6; then
|
|
res_ok "Qt6 - via qmake6 (graphs, 'hw tune' plot)"
|
|
elif pc_exists Qt5Core Qt5Widgets; then
|
|
res_ok "Qt5 - $(pc_ver Qt5Core) (graphs, build with SKIPQT6=1)"
|
|
else
|
|
miss_opt qt "Qt - not found, the graphical window is disabled"
|
|
fi
|
|
|
|
check_lib gd "libgd - NFC ePaper devices" opt gdlib gd.h -lgd
|
|
check_lib opencl "OpenCL - only for the optional crack5opencl tool" note OpenCL ""
|
|
|
|
section "Client libraries (bundled fallback, system copy only speeds up the build)"
|
|
|
|
for pair in "jansson:jansson" "whereami:whereami" "lua:lua5.4"; do
|
|
nm="${pair%%:*}"
|
|
pc="${pair##*:}"
|
|
if pc_exists "$pc" || { [ "$nm" = "lua" ] && pc_exists lua-5.4; }; then
|
|
res_ok "$nm - system copy found"
|
|
elif [ "$nm" = "whereami" ] && [ -f /usr/include/whereami.h ]; then
|
|
res_ok "whereami - system copy found"
|
|
else
|
|
res_skip "$nm - not installed, the bundled copy in client/deps will be used"
|
|
fi
|
|
done
|
|
|
|
# --- 6. python environment ---------------------------------------------------
|
|
|
|
section "Python"
|
|
|
|
PYTHON3="${PYTHON3:-python3}"
|
|
VENV_DIR=""
|
|
VENV_PY=""
|
|
VENV_ORIGIN=""
|
|
|
|
venv_python() {
|
|
if [ -x "$1/bin/python3" ]; then
|
|
echo "$1/bin/python3"
|
|
elif [ -x "$1/bin/python" ]; then
|
|
echo "$1/bin/python"
|
|
elif [ -x "$1/Scripts/python.exe" ]; then
|
|
echo "$1/Scripts/python.exe"
|
|
fi
|
|
}
|
|
|
|
if have "$PYTHON3"; then
|
|
res_ok "python3 - $(first_line "$PYTHON3" --version)"
|
|
|
|
if "$PYTHON3" -c 'import venv' >/dev/null 2>&1; then
|
|
res_ok "venv module available"
|
|
else
|
|
miss_opt venv "python3 'venv' module missing - cannot create a virtualenv"
|
|
fi
|
|
|
|
if "$PYTHON3" -m pip --version >/dev/null 2>&1; then
|
|
res_ok "pip available"
|
|
else
|
|
miss_opt pip "pip not available for $PYTHON3"
|
|
fi
|
|
else
|
|
miss_opt python3 "python3 - not found"
|
|
res_hint "only the Python-based tools and client scripting need it"
|
|
fi
|
|
|
|
if [ "$NO_VENV" = "true" ]; then
|
|
res_skip "venv search disabled (--no-venv), checking the python3 on PATH"
|
|
VENV_PY="$(have "$PYTHON3" && command -v "$PYTHON3")"
|
|
VENV_ORIGIN="system"
|
|
else
|
|
for cand_pair in \
|
|
"${VENV_OPT}:asked for" \
|
|
"${VIRTUAL_ENV:-}:currently active" \
|
|
"$PM3PATH/venv:in the repo" \
|
|
"$HOME/.venv/proxmark3:in \$HOME/.venv"
|
|
do
|
|
cand="${cand_pair%%:*}"
|
|
why="${cand_pair#*:}"
|
|
[ -n "$cand" ] || continue
|
|
|
|
if [ -f "$cand/pyvenv.cfg" ]; then
|
|
VENV_DIR="$cand"
|
|
VENV_ORIGIN="$why"
|
|
break
|
|
fi
|
|
|
|
if [ "$why" = "asked for" ]; then
|
|
res_fail "no venv at $cand"
|
|
res_hint "create it with: $PYTHON3 -m venv \"$cand\""
|
|
break
|
|
fi
|
|
done
|
|
fi
|
|
|
|
if [ -n "$VENV_DIR" ]; then
|
|
VENV_PY="$(venv_python "$VENV_DIR")"
|
|
if [ -n "$VENV_PY" ]; then
|
|
res_ok "venv $VENV_ORIGIN - $VENV_DIR ($("$VENV_PY" --version 2>&1))"
|
|
else
|
|
res_fail "venv at $VENV_DIR has no usable interpreter"
|
|
res_hint "delete it and let this script create a new one"
|
|
VENV_DIR=""
|
|
fi
|
|
fi
|
|
|
|
CREATE_VENV_AT=""
|
|
if [ "$NO_VENV" != "true" ] && [ -z "$VENV_DIR" ] && [ -z "$VENV_OPT" ]; then
|
|
res_warn "no Python venv found"
|
|
res_hint "the Python tools and 'make check' want the packages in tools/requirements.txt,"
|
|
res_hint "and most distros refuse a system-wide pip install (PEP 668)"
|
|
|
|
if have "$PYTHON3" && "$PYTHON3" -c 'import venv' >/dev/null 2>&1; then
|
|
if ask " Create one in the repo folder, at $PM3PATH/venv?"; then
|
|
CREATE_VENV_AT="$PM3PATH/venv"
|
|
else
|
|
res_hint "skipped. Create one later with: $PYTHON3 -m venv venv"
|
|
res_hint "or point this script at an existing one: --venv /path/to/venv"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$CREATE_VENV_AT" ]; then
|
|
printf ' creating venv at %s ...\n' "$CREATE_VENV_AT"
|
|
if "$PYTHON3" -m venv "$CREATE_VENV_AT"; then
|
|
VENV_DIR="$CREATE_VENV_AT"
|
|
VENV_ORIGIN="just created"
|
|
VENV_PY="$(venv_python "$VENV_DIR")"
|
|
res_ok "venv created - $VENV_DIR"
|
|
res_hint "activate it with: source ${VENV_DIR#"$PM3PATH"/}/bin/activate"
|
|
else
|
|
res_fail "could not create a venv at $CREATE_VENV_AT"
|
|
fi
|
|
fi
|
|
|
|
# requirements
|
|
REQ_FILES="tools/requirements.txt"
|
|
REQ_EXTRA="client/requirements.txt fpga/requirements.txt tools/ePassport/requirements.txt tools/btaddon/requirements.txt"
|
|
|
|
check_requirements() {
|
|
local py="$1" reqfile="$2" required="$3" out line
|
|
|
|
if [ ! -f "$PM3PATH/$reqfile" ]; then
|
|
return 0
|
|
fi
|
|
|
|
if [ -z "$py" ]; then
|
|
res_skip "$reqfile - no interpreter to check against"
|
|
return 0
|
|
fi
|
|
|
|
out="$("$py" -I - "$PM3PATH/$reqfile" <<'PYEOF'
|
|
import re
|
|
import sys
|
|
|
|
try:
|
|
from importlib.metadata import PackageNotFoundError, version
|
|
except ImportError:
|
|
print("SKIP metadata api unavailable")
|
|
sys.exit(0)
|
|
|
|
|
|
def as_tuple(s):
|
|
return tuple(int(p) if p.isdigit() else 0 for p in re.split(r"[._-]+", s))
|
|
|
|
|
|
def aligned(a, b):
|
|
n = max(len(a), len(b))
|
|
return a + (0,) * (n - len(a)), b + (0,) * (n - len(b))
|
|
|
|
|
|
SPEC = re.compile(r"^([A-Za-z0-9._-]+)\s*(==|>=|<=|~=|>|<)?\s*([0-9][^\s,]*)?")
|
|
|
|
for path in sys.argv[1:]:
|
|
with open(path) as fd:
|
|
for raw in fd:
|
|
text = raw.split("#", 1)[0].split(";", 1)[0].strip()
|
|
if not text:
|
|
continue
|
|
|
|
m = SPEC.match(text)
|
|
if m is None:
|
|
print("SKIP", text)
|
|
continue
|
|
|
|
name, op, want = m.group(1), m.group(2), m.group(3)
|
|
try:
|
|
got = version(name)
|
|
except PackageNotFoundError:
|
|
print("MISSING", name, (op or "") + (want or ""))
|
|
continue
|
|
except Exception:
|
|
print("SKIP", name)
|
|
continue
|
|
|
|
have, need = aligned(as_tuple(got), as_tuple(want))
|
|
if op == "==" and have != need:
|
|
print("OTHER", name, got, op + want)
|
|
elif op in (">=", "~=") and have < need:
|
|
print("OLD", name, got, op + want)
|
|
else:
|
|
print("OK", name, got)
|
|
PYEOF
|
|
)"
|
|
|
|
local n_missing=0 n_other=0 n_ok=0 names=""
|
|
while IFS= read -r line; do
|
|
[ -n "$line" ] || continue
|
|
case "$line" in
|
|
OK*) n_ok=$((n_ok + 1)) ;;
|
|
MISSING*) n_missing=$((n_missing + 1)); names="$names $(echo "$line" | cut -d' ' -f2)" ;;
|
|
OLD*|OTHER*) n_other=$((n_other + 1)); names="$names $(echo "$line" | cut -d' ' -f2)" ;;
|
|
SKIP*) ;;
|
|
esac
|
|
done <<EOT
|
|
$out
|
|
EOT
|
|
|
|
if [ "$n_missing" -eq 0 ] && [ "$n_other" -eq 0 ]; then
|
|
res_ok "$reqfile - all $n_ok package(s) installed"
|
|
return 0
|
|
fi
|
|
|
|
local msg="$reqfile - $n_missing missing, $n_other at another version:$names"
|
|
if [ "$required" = "req" ]; then
|
|
res_warn "$msg"
|
|
else
|
|
res_skip "$msg"
|
|
fi
|
|
|
|
res_hint "install with: ${py#"$PM3PATH"/} -m pip install -r $reqfile"
|
|
return 1
|
|
}
|
|
|
|
PIP_TODO=""
|
|
if check_requirements "$VENV_PY" "$REQ_FILES" req; then
|
|
:
|
|
else
|
|
PIP_TODO="$REQ_FILES"
|
|
fi
|
|
|
|
if [ "$WANT_EXTRAS" = "true" ]; then
|
|
for f in $REQ_EXTRA; do
|
|
check_requirements "$VENV_PY" "$f" opt || PIP_TODO="$PIP_TODO $f"
|
|
done
|
|
else
|
|
res_info "optional requirement files not checked - rerun with --extras"
|
|
res_hint "$REQ_EXTRA"
|
|
fi
|
|
|
|
# with --extras, client/requirements.txt was already reported just above
|
|
if [ "$PLATFORM_VAL" = "PM5" ] && [ "$WANT_EXTRAS" != "true" ]; then
|
|
if [ -n "$VENV_PY" ] && "$VENV_PY" -I -c 'import bleak' >/dev/null 2>&1; then
|
|
res_ok "bleak - the BLE roles of client/pm5_ble_bridge.py are available"
|
|
else
|
|
res_skip "bleak not installed - only client/pm5_ble_bridge.py BLE roles need it"
|
|
res_hint "native 'pm3 -p ble:<MAC>' and wifi 'pm3 -p tcp:' do not"
|
|
|
|
if [ -n "$VENV_PY" ]; then
|
|
res_hint "install with: ${VENV_PY#"$PM3PATH"/} -m pip install -r client/requirements.txt"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if have uv; then
|
|
res_ok "uv - $(first_line uv --version) (tools/pm3_tests.sh uses it when present)"
|
|
else
|
|
res_skip "uv not installed - tools/pm3_tests.sh falls back to python3"
|
|
fi
|
|
|
|
# --- 7. device access --------------------------------------------------------
|
|
|
|
section "Device access"
|
|
|
|
if [ "$OS" = "Linux" ]; then
|
|
if [ -f /etc/arch-release ]; then
|
|
DEV_GROUP="uucp"
|
|
else
|
|
DEV_GROUP="dialout"
|
|
fi
|
|
|
|
if id -nG 2>/dev/null | tr ' ' '\n' | grep -qx "$DEV_GROUP"; then
|
|
res_ok "user is in the '$DEV_GROUP' group"
|
|
else
|
|
res_warn "user is not in the '$DEV_GROUP' group - /dev/ttyACM0 will be denied"
|
|
res_hint "fix with: make accessrights (then log out and back in)"
|
|
NEED_ACCESSRIGHTS=true
|
|
fi
|
|
|
|
# 'make accessrights' adds this one too, when the group exists
|
|
if getent group bluetooth >/dev/null 2>&1; then
|
|
case "$PLATFORM_VAL $EXTRAS_VAL" in
|
|
*PM5*|*BTADDON*) BT_SEVERITY="warn" ;;
|
|
*) BT_SEVERITY="note" ;;
|
|
esac
|
|
|
|
if id -nG 2>/dev/null | tr ' ' '\n' | grep -qx bluetooth; then
|
|
res_ok "user is in the 'bluetooth' group - 'pm3 -p bt:' and 'ble:' can connect"
|
|
elif [ "$BT_SEVERITY" = "warn" ]; then
|
|
res_warn "user is not in the 'bluetooth' group - 'pm3 -p bt:' / 'ble:' may be denied"
|
|
res_hint "fix with: make accessrights (then log out and back in)"
|
|
NEED_ACCESSRIGHTS=true
|
|
else
|
|
res_skip "user is not in the 'bluetooth' group - only matters for 'pm3 -p bt:' / 'ble:'"
|
|
fi
|
|
fi
|
|
|
|
if [ -f /etc/udev/rules.d/77-pm3-usb-device-blacklist.rules ] || \
|
|
[ -f /lib/udev/rules.d/77-pm3-usb-device-blacklist.rules ]; then
|
|
res_ok "udev rule 77-pm3-usb-device-blacklist.rules installed"
|
|
else
|
|
res_warn "udev rule not installed"
|
|
res_hint "fix with: make udev"
|
|
NEED_UDEV=true
|
|
fi
|
|
|
|
if have systemctl && systemctl is-active --quiet ModemManager 2>/dev/null; then
|
|
res_warn "ModemManager is running - it can brick the Proxmark3 while flashing"
|
|
res_hint "read doc/md/Installation_Instructions/ModemManager-Must-Be-Discarded.md"
|
|
res_hint "the udev rule above only helps when ModemManager is not in strict mode"
|
|
else
|
|
res_ok "ModemManager not running"
|
|
fi
|
|
|
|
PM3_PORT=""
|
|
for p in /dev/ttyACM0 /dev/ttyACM1 /dev/ttyS* ; do
|
|
[ -c "$p" ] || continue
|
|
case "$p" in /dev/ttyACM*) PM3_PORT="$p"; break ;; esac
|
|
done
|
|
|
|
if [ -n "$PM3_PORT" ]; then
|
|
if [ -r "$PM3_PORT" ] && [ -w "$PM3_PORT" ]; then
|
|
res_ok "$PM3_PORT present and read/writable"
|
|
else
|
|
res_warn "$PM3_PORT present but not read/writable by this user"
|
|
fi
|
|
else
|
|
res_info "no /dev/ttyACM* found - plug in over USB and rerun to check those rights"
|
|
res_hint "a wireless link ('pm3 -p tcp:' / 'ble:' / 'bt:') does not use /dev/ttyACM*,"
|
|
res_hint "but the rules above still matter: every Proxmark gets flashed over USB"
|
|
fi
|
|
|
|
# ble_resolve_name() opens a raw HCI socket; client/Makefile sets this on install
|
|
PM3BIN="$(command -v proxmark3 2>/dev/null)"
|
|
if [ -n "$PM3BIN" ]; then
|
|
GETCAP="$(command -v getcap 2>/dev/null)"
|
|
[ -n "$GETCAP" ] || GETCAP=/usr/sbin/getcap
|
|
|
|
if [ -x "$GETCAP" ]; then
|
|
if "$GETCAP" "$PM3BIN" 2>/dev/null | grep -q cap_net_raw; then
|
|
res_ok "installed client has cap_net_raw - 'pm3 -p ble:<name>' works unprivileged"
|
|
else
|
|
res_skip "installed client has no cap_net_raw - 'pm3 -p ble:<name>' needs sudo, 'ble:<MAC>' does not"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ "$IS_WSL" = "true" ]; then
|
|
res_info "WSL - USB devices need usbipd-win on the Windows side"
|
|
res_hint "doc/md/Installation_Instructions/Windows-WSL2-Installation-Instructions.md"
|
|
fi
|
|
else
|
|
res_skip "device access checks are Linux only"
|
|
fi
|
|
|
|
# --- 8. developer extras -----------------------------------------------------
|
|
|
|
section "Developer extras (only needed to submit changes)"
|
|
|
|
check_cmd astyle astyle "astyle - 'make style' source formatting" note
|
|
check_cmd recode recode "recode - 'make miscchecks' encoding checks" note
|
|
|
|
# --- install -----------------------------------------------------------------
|
|
|
|
INSTALL_KEYS="$MISSING_REQ $MISSING_OPT"
|
|
INSTALL_PKGS=""
|
|
# nix and unrecognised systems resolve to no package names at all, so the keys,
|
|
# not the names, decide whether there is anything to report
|
|
ANY_MISSING="$(echo "$INSTALL_KEYS" | tr -d ' ')"
|
|
[ -n "$ANY_MISSING" ] && INSTALL_PKGS="$(pkgs_for_keys "$INSTALL_KEYS")"
|
|
|
|
if [ -n "$ANY_MISSING" ]; then
|
|
if [ -n "$(echo "$MISSING_REQ" | tr -d ' ')" ]; then
|
|
section "Packages to install"
|
|
else
|
|
section "Optional packages, nothing here blocks the build"
|
|
fi
|
|
|
|
if [ "$PKG_MGR" = "nix" ]; then
|
|
printf ' %s\n' "nix-shell tools/shell.nix"
|
|
printf ' %s\n' "brings in everything needed to build the client and the firmware"
|
|
elif [ -z "$INSTALL_PKGS" ]; then
|
|
printf ' %s\n' "no package names known for this system"
|
|
printf ' %s\n' "install the items marked fail or warn above, see doc/md/Installation_Instructions/"
|
|
else
|
|
printf ' %s\n' "$(pkg_install_cmd "$INSTALL_PKGS")"
|
|
fi
|
|
|
|
if [ "$DO_INSTALL" = "true" ] && [ -n "$INSTALL_PKGS" ] && [ "$PKG_MGR" != "nix" ]; then
|
|
echo
|
|
if ask "Run that now?"; then
|
|
case "$PKG_MGR" in
|
|
apt) sudo apt-get install --no-install-recommends $INSTALL_PKGS ;;
|
|
pacman) sudo pacman -Syu --needed $INSTALL_PKGS ;;
|
|
dnf) sudo dnf install $INSTALL_PKGS ;;
|
|
zypper) sudo zypper install $INSTALL_PKGS ;;
|
|
brew) brew install $INSTALL_PKGS ;;
|
|
port) sudo port install $INSTALL_PKGS ;;
|
|
esac
|
|
res_info "rerun ./configure_environment.sh to confirm"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$(echo "$PIP_TODO" | tr -d ' ')" ] && [ -n "$VENV_PY" ]; then
|
|
if [ "$DO_INSTALL" = "true" ] || [ "$VENV_ORIGIN" = "just created" ]; then
|
|
section "Python requirements"
|
|
for f in $PIP_TODO; do
|
|
printf ' %s\n' "$VENV_PY -m pip install -r $f"
|
|
done
|
|
|
|
if ask "Install them into $VENV_DIR?"; then
|
|
for f in $PIP_TODO; do
|
|
"$VENV_PY" -m pip install -r "$PM3PATH/$f" || res_warn "pip failed for $f"
|
|
done
|
|
res_info "rerun ./configure_environment.sh to confirm"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
# --- summary -----------------------------------------------------------------
|
|
|
|
section "Summary"
|
|
|
|
printf ' passed : %s\n' "$N_OK"
|
|
printf ' warnings : %s%s\n' "$N_WARN" "$([ "$N_WARN" -gt 0 ] && echo ' (optional features will be disabled)')"
|
|
printf ' failures : %s%s\n' "$N_FAIL" "$([ "$N_FAIL" -gt 0 ] && echo ' (the build will not complete)')"
|
|
echo
|
|
|
|
if [ "$N_FAIL" -gt 0 ]; then
|
|
printf '%s\n' "${C_RED}Something required is missing.${C_RST} Install the packages listed above and rerun."
|
|
exit 1
|
|
fi
|
|
|
|
printf '%s\n' "${C_GRN}Ready to build.${C_RST}"
|
|
printf '%s\n' " make clean && make -j build client and firmware"
|
|
printf '%s\n' " make check run the offline tests"
|
|
if [ "$NEED_ACCESSRIGHTS" = "true" ]; then
|
|
printf '%s\n' " make accessrights add yourself to the '$DEV_GROUP' group"
|
|
fi
|
|
|
|
if [ "$NEED_UDEV" = "true" ]; then
|
|
printf '%s\n' " make udev keep ModemManager off the device"
|
|
fi
|
|
printf '%s\n' " ./pm3 start the client"
|
|
echo
|
|
printf '%s\n' "More detail in doc/md/Use_of_Proxmark/0_Compilation-Instructions.md"
|
|
|
|
exit 0
|