#!/bin/bash usage() { cat << EOF Usage: ${SELF} [-d ] [-p] [l|list] [d|download|da|download-all] [e|extract-config] options -d save all downloaded files to (default ~/fw-avm/opensrc) -p prefer previously fetched ftp server content listing actions l|list fetch content listing of AVM's ftp server d|download download opensrc packages of up-to-date firmwares only (implies "list" if -p is not specified) da|download-all download opensrc packages of all firmwares (implies "list" if -p is not specified) e|extract-config extract kernel .config's of all opensrc-packages in download-dir EOF } # # $1 - ftp-dir to be (recursively) listed # ftp-dir is expected to have the following format: ftp://some.site/some/dir/ # 'ftp://' might be omitted # # returns ftp server content listing as string, one file per line # function list_ftp_dir() { if [ $# -ne 1 -o -z "$1" ]; then echo "Error[${FUNCNAME}]: ${FUNCNAME} expects exactly one non-empty parameter" >&2 exit 1 fi if ! which lftp >/dev/null 2>&1; then echo "Error[${FUNCNAME}]: ${FUNCNAME} requires lftp to be installed" >&2 exit 1 fi local url="${1#ftp://}" [ "${url: -1: 1}" != "/" ] && url="${url}/" if [ "${url//:\/\/}" != "${url}" ]; then echo "Error[${FUNCNAME}]: unsupported protocol in '${url}', only ftp:// is supported" >&2 exit 1 fi local ftp_site="${url%%/*}" local ftp_dir="/${url#*/}" local lftp_output=$(lftp -e "find ${ftp_dir}; bye" ${ftp_site}) if [ $? != 0 ]; then echo "Error[${FUNCNAME}]: failed to list content of '${url}'" >&2 exit 1 fi echo "$lftp_output" | sed -r -e '/\/$/ d' -e "s,^,ftp://${ftp_site}," #-e 's, ,%20,g' } # # $1 - list of URLs, one URL per line # # returns list of URLs of all opensrc packages, one URL per line # function get_opensrc_packages() { if [ $# -ne 1 -o -z "$1" ]; then echo "Error[${FUNCNAME}]: ${FUNCNAME} expects exactly one parameter" >&2 exit 1 fi echo "$1" | grep -E "/fritzbox[._]" | grep "opensrc" } # # $1 - opensrc package URL # # returns ID of opensrc package, e.g. 7270v3.05.51 # function get_opensrc_package_id() { if [ $# -ne 1 -o -z "$1" ]; then echo "Error[${FUNCNAME}]: ${FUNCNAME} expects exactly one parameter" >&2 exit 1 fi echo "$1" | sed -r -e 's,.*([0-9]{4})(_(sl|v[1-3]|lte))?/.*(0[4-6][.][0-9]{2})(-([0-9]{2}))?[.]tar[.]gz,\1\3.\4,' } # # $1 - any string containing 04.xx, 05.[25]x, 06.xx as substring # function is_uptodate_firmware() { echo "$1" | grep -qE "(0[46][.][0-9]{2}|05[.][25][0-9])" } # # $1..$* - path(s) to AVM open-source tarball(s) # function extract_kernel_config() { local opensrc_tarball=, kernel_tarball=, id=, tmp=, config=, makefile= for opensrc_tarball in "$@"; do id="$(basename "${opensrc_tarball%.tar.gz}")" echo -n "${id}: " tmp=$(mktemp -q -d /tmp/${id}-XXXXXX) if [ $? != 0 ]; then echo "failed to create temporary dir" shift continue fi echo -n "extracting opensrc-tarball ... " tar -C ${tmp} -xaf ${opensrc_tarball} --wildcards '*kernel*.tar.gz' >/dev/null 2>&1 echo -n "searching for kernel sources ... " kernel_tarball=$(find ${tmp} -name '*kernel*.tar.gz') if [ -z "${kernel_tarball}" ]; then echo -n "not found ... assuming opensrc-tarball is kernel-tarball ... " kernel_tarball="${opensrc_tarball}" else echo -n "found ${kernel_tarball#${tmp}/} ... " fi echo -n "searching for .config ... " config=$(tar -C ${tmp} -taf ${kernel_tarball} | grep -E "[.]config$") if [ $? -ne 0 ]; then echo "failed" else makefile="${config%.config}Makefile" echo -n "found ${config} ... extracting ... " if ! tar -C ${tmp} -xaf ${kernel_tarball} ${config} ${makefile}; then echo "failed" else unset VERSION PATCHLEVEL SUBLEVEL EXTRAVERSION cat ${tmp}/${makefile} | sed -n -r -e "s,^(VERSION|PATCHLEVEL|SUBLEVEL|EXTRAVERSION)[ \t]*=[ \t]*([^ \t]+),\1=\2,p" > ${tmp}/${makefile}.version source ${tmp}/${makefile}.version mv ${tmp}/${config} "$(dirname "${opensrc_tarball}")/.config-AVM-${id}-${VERSION}.${PATCHLEVEL}.${SUBLEVEL}${EXTRAVERSION}" && echo "done" || echo "failed" fi fi rm -rf ${tmp} shift done } # # returns current timestamp in YYYYMMDD-HHMMSS format # function get_current_timestamp() { date +%Y%m%d-%H%M%S } ############################################ SELF=$(basename "$0") DL_DIR=~/fw-avm/opensrc PREFER_PREVIOUS_LISTING= DO_FETCH_LISTING= DO_DOWNLOAD= DO_EXTRACT_CONFIG= # parse options while getopts d:p opt; do case "$opt" in d) DL_DIR="$OPTARG" ;; p) PREFER_PREVIOUS_LISTING=true ;; *) usage >&2; exit 1 ;; esac done shift $((OPTIND-1)) # parse actions for opt in "$@"; do case "$opt" in l|list) if [ -n "$PREFER_PREVIOUS_LISTING" ]; then echo "Error: specifying both '-p' and 'list' at the same time doesn't make sense" >&2 usage >&2 exit 1 fi DO_FETCH_LISTING=true ;; d|download) DO_DOWNLOAD=recent-only [ -z "$PREFER_PREVIOUS_LISTING" ] && DO_FETCH_LISTING=true ;; da|download-all) DO_DOWNLOAD=all [ -z "$PREFER_PREVIOUS_LISTING" ] && DO_FETCH_LISTING=true ;; e|extract-config) DO_EXTRACT_CONFIG=false ;; *) usage >&2; exit 1 ;; esac done # check if at least one action is specified [ -n "$DO_FETCH_LISTING" -o -n "$DO_DOWNLOAD" -o -n "$DO_EXTRACT_CONFIG" ] || { usage >&2; exit 1; } # create DL_DIR if missing [ ! -e "$DL_DIR" ] && mkdir -p "$DL_DIR" ( cd "$DL_DIR" || exit 1 # check if curl is available as early as possible if [ -n "$DO_DOWNLOAD" ] && ! which curl >/dev/null 2>&1; then echo "Error: ${SELF} requires curl to be installed" >&2 exit 1 fi # check if any previously fetched listing is available and use it if requested if [ -n "$PREFER_PREVIOUS_LISTING" ]; then listing_fn=$(ls ftp.avm.de-fritz.box-????????-??????.listing 2>/dev/null) if [ $? -ne 0 ]; then echo "No previously fetched listing found, forcing fetching of ftp server content listing" DO_FETCH_LISTING=true else listing_fn=$(echo "$listing_fn" | sort | tail -n 1) echo "Using previously fetched ftp server content listing from $listing_fn" listing=$(cat "$listing_fn") fi fi # fetch ftp server content listing and save it to a file if [ -n "$DO_FETCH_LISTING" ]; then listing="$(list_ftp_dir ftp.avm.de/fritzbox/)" echo "$listing" > ftp.avm.de-fritzbox-$(get_current_timestamp).listing fi # download opensrc packages, avoid redownloading of already downloaded files if [ -n "$DO_DOWNLOAD" ]; then get_opensrc_packages "$listing" | while read url; do id=$(get_opensrc_package_id "$url") if [ "$DO_DOWNLOAD" == all ] || is_uptodate_firmware "${id}"; then #TODO: progress bar curl -R -C - -o "${id}.tar.gz" "${url}" else echo "Skipping opensrc package ${url} - old firmware version" fi done fi # extract kernel .config's if [ -n "$DO_EXTRACT_CONFIG" ]; then extract_kernel_config *.tar.gz fi )