#!/bin/bash SOURCE="$0" source /usr/lib/elive-tools/functions EL_REPORTS="1" el_make_environment . gettext.sh TEXTDOMAIN="usb_bootable_elive" export TEXTDOMAIN guitool="/usr/bin/zenity --window-icon=/usr/share/icons/gnome/256x256/apps/logo-elive.png" url_elive="www.elivecd.org" cache_dir="$HOME/.cache/$(basename $0)" TMP_PROGRESS_WORKING_f="${cache_dir}/progress-file-working" TMP_ERROR_LOGS_f="${cache_dir}/logs-error.txt" wgetopts="--tries=40 --progress=dot:binary -c" #set -E # include sbin in our PATH since its needed sometimes, and there's nothing wrong by using it! if [[ "$PATH" != *"/usr/sbin"* ]] ; then # needed for: udevadm export PATH="${PATH}:/usr/local/sbin:/usr/sbin:/sbin" fi # TODO: add an option for improve the health of the USB (filling it with zeroes, which sometimes are needed, more than we think!, which also guarantees the correct writting) # TODO: recyclate persistence and other partitions # Lock system (good one) {{{ lockfile="/tmp/.$(basename $0)-${USER}.lock" exit_ok(){ guitool working_stop rm -f "$lockfile" kill "$pid" 2>/dev/null || kill -9 "$pid" 2>/dev/null || true rm -f "$TMP_ERROR_LOGS_f" "$TMP_PROGRESS_WORKING_f" } exit_error(){ guitool working_stop rm -f "$lockfile" kill "$pid" 2>/dev/null || kill -9 "$pid" 2>/dev/null || true if [[ -s "$TMP_ERROR_LOGS_f" ]] ; then guitool error "$( eval_gettext "Aborted USB writing." )\n\n$(tail -2 "$TMP_ERROR_LOGS_f")" else guitool error "$( eval_gettext "Aborted USB writing." )" fi guitool info "$( eval_gettext "If there's an issue with the tool we suggest to report it to Elive. You can also try deleting the directory:" ) $cache_dir" rm -f "$TMP_ERROR_LOGS_f" "$TMP_PROGRESS_WORKING_f" } if [[ -r "$lockfile" ]] ; then PROCCESS="$(cat $lockfile)" else PROCCESS=" " fi if (ps up $PROCCESS) 1>/dev/null 2>&1 ; then NOREPORTS=1 el_error "$(basename $0) already running" exit else echo $$ > "$lockfile" fi # traps needs to be after the lock verification, in order to not remove it when we are already running trap "exit_ok" EXIT trap "exit_error" 1 3 5 6 14 15 ERR TERM # SET the lock file echo "$$" > "$lockfile" # }}} # this function generates the menu with the USB device to select generate_menu_selector(){ local buf size label is_valid # build menu variable from partitions list {{{ # device :: type :: filesystem :: label :: human_label :: size :: human_size :: uuid if [[ "$1" = "include_hd" ]] ; then regex_matches="^(ID_USB_DRIVER=usb-storage|DEVPATH=.*/usb|ID_TYPE=disk|ID_DRIVE.*FLASH_SD=1)$" else regex_matches="^(ID_USB_DRIVER=usb-storage|DEVPATH=.*/usb)$" fi unset menu id_vendor while read -ru 3 entry do unset buf size label is_valid part="$( echo "$entry" | awk -v FS="::" '{print $1}' )" [[ ! -b "$part" ]] && continue # ignore partitions (end in numbers) case "$part" in "/dev/mmcblk"*) echo "$part" | grep -qsE "p[[:digit:]]+$" && continue ;; "/dev/nvme"*) echo "$part" | grep -qsE "(e|p)[[:digit:]]+$" && continue ;; *) echo "$part" | grep -qsE "[[:digit:]]+$" && continue ;; esac buf="$( EL_DEBUG=0 partitions-list --show-raw --show-disks --show-only="$part" )" size="$( echo "$buf" | awk -v FS="::" '{print $6}' )" label="$( echo "$buf" | awk -v FS="::" '{print $4}' )" # check for disks which has already been labeled as Elive USB's if [[ "$label" = Elive_*_at_* ]] ; then is_valid=1 fi if udevadm info --query=property --name="$part" | grep -qsE "$regex_matches" || ((is_valid)) ; then # skip big disks when not searching for HDs if [[ "$1" != "include_hd" ]] && ! ((is_valid)) ; then if [[ "$size" -gt 350000000000 ]] ; then el_debug "Disk too big, ignoring because we are searching only for USB sticks: $part" continue fi fi # create an array for use in the menu, with one per line menu+=("$( echo "$entry" | awk -v FS="::" '{print $1}' )") menu+=("$( echo "$entry" | awk -v FS="::" '{print $7}' )") id_vendor="$( udevadm info --query=property --name="$part" | grep "ID_VENDOR_ID=" | sed -e 's|^.*VENDOR_ID=||g' )" id_vendor="$( lsusb | grep "ID ${id_vendor}:" | sed -e "s|^.*${id_vendor}:||g" | awk '{print $2" "$3}' )" if [[ -z "$id_vendor" ]] ; then id_vendor="$( udevadm info --query=property --name="$part" | grep "ID_MODEL=" | sed -e 's|^.*ID_MODEL=||g' | awk '{print $1" "$2}' )" fi if echo "$entry" | awk -v FS="::" '{print $5}' | grep -qsE "(no name|swap|unformatted)" ; then menu+=("$( echo "$entry" | awk -v FS="::" -v id_vendor="$id_vendor" '{print id_vendor}' | sort -u | tr '\n' ' ' )") else menu+=("$( echo "$entry" | awk -v FS="::" -v id_vendor="$id_vendor" '{print id_vendor " - "$5}' | sort -u | tr '\n' ' ' )") fi fi done 3<<< "$( EL_DEBUG=0 partitions-list --show-all --show-disks --show-raw | awk -v FS="::" '{if ($3 != "swap") print $0}' )" # }}} } #=== FUNCTION ================================================================ # NAME: guitool # DESCRIPTION: small tool for interact with the user graphically # PARAMETERS: $1 = mode, $2 = message, $3 = "%s" equivalent vars for $2 # RETURNS: value, if needed #=============================================================================== guitool(){ # pre {{{ local mode message #el_debug mode="$1" message="$2" el_check_variables "mode|guitool" #el_debug " $FUNCNAME $@" # }}} # functions {{{ # This is not going to work here because gettext is unable to get the messages from a dynamic variable # local _translated_message # _translated_message="$( printf "$( eval_gettext "$message" )" "$@" )" case "$mode" in info) guitool working_stop el_check_variables "message" $guitool --info --text="$message" ;; error) guitool working_stop el_check_variables "message" $guitool --error --text="$message" ;; warning) guitool working_stop el_check_variables "message" $guitool --warning --text="$message" ;; working_write_progress) if ((is_auto_mode)) ; then return 0 fi echo "1" > "$TMP_PROGRESS_WORKING_f" sync if [[ -n "$message" ]] ; then message_doing_magic="$message" else # random message generator message_doing_magic="$( eval_gettext "Elive is doing some magic..." )" # random message generator case "$( shuf -i 1-5 -n 1 )" in 1) message_doing_magic="$( eval_gettext "Elive is doing some magic..." )" ;; 2) message_doing_magic="$( eval_gettext "Transferring bytes at the speed of light..." )" ;; 3) message_doing_magic="$( eval_gettext "Arranging some atoms in the universe..." )" ;; 4) message_doing_magic="$( eval_gettext "Mutating frogs with unicorns..." )" ;; 5) message_doing_magic="Dancing 'Staying Elive' by the Bee Gees..." ;; esac fi { ( while test -f "${TMP_PROGRESS_WORKING_f}" ; do tail -n 1 "$TMP_PROGRESS_WORKING_f" | sed -e 's;\(100\|99\|98\|97\|96\);95;g' || true ; LC_ALL=C sleep 0.5 ; done | $guitool --progress --text="$message_doing_magic" --auto-close --no-cancel ) & disown ; } 1>/dev/null ;; working_start) if ((is_auto_mode)) ; then return 0 fi # already running? keep it running instead of creating a new one if [[ -s "$TMP_PROGRESS_WORKING_f" ]] || ((is_guitool_working_running)) ; then return 0 fi echo working > "$TMP_PROGRESS_WORKING_f" sync is_guitool_working_running=1 if [[ -n "$message" ]] ; then message_doing_magic="$message" else # random message generator message_doing_magic="$( eval_gettext "Elive is doing some magic..." )" # random message generator case "$( shuf -i 1-5 -n 1 )" in 1) message_doing_magic="$( eval_gettext "Elive is doing some magic..." )" ;; 2) message_doing_magic="$( eval_gettext "Transferring bytes at the speed of light..." )" ;; 3) message_doing_magic="$( eval_gettext "Arranging some atoms in the universe..." )" ;; 4) message_doing_magic="$( eval_gettext "Mutating frogs with unicorns..." )" ;; 5) message_doing_magic="Dancing 'Staying Elive' by the Bee Gees..." ;; esac fi { ( while test -s "$TMP_PROGRESS_WORKING_f" ; do echo 10 ; tail -n 1 "$TMP_PROGRESS_WORKING_f" || true ; sleep 1 ; done | $guitool --progress --text="${message_doing_magic}\n\n${message_doing_magic_extra}" --pulsate --auto-close ) & disown ; } 2>/dev/null ;; working_stop) if ((is_auto_mode)) ; then return 0 fi rm -f "$TMP_PROGRESS_WORKING_f" unset is_guitool_working_running ;; question) guitool working_stop el_check_variables "message" if $guitool --question --text="$message" ; then return 0 else return 1 fi ;; esac # }}} } usage(){ # options {{{ echo -e "$(basename $0) can record an ISO/IMG to a USB, compatible with most of images / isos even not Elive ones, the tool is interactive unless:" echo -e "\nOptions:" echo -e "--auto Do not ask for USB device, just use the one that you have actually inserted" echo -e "--simulate Simulation mode, does not write real data to the device" echo -e "--fast Fast writing, progress bar will not be syncronized" echo -e "elive.iso|img Use the given image to record" # - options }}} } main(){ # pre {{{ local url_image_stable url_image_beta menu id_vendor opts_dd_sync="oflag=dsync" el_dependencies_check "dd|lynx|wget|zsync" for arg in "$@" do case "$arg" in -h|--help) echo -e "This tool will record Elive in a USB, automatically downloading the last version and asking for your USB device where to record it." echo -e " * if you use the --auto option, the USB is automatically detected and not asked" echo -e " * if you give the image as parameter, it will not download it but use the given one" echo -e "Example: $(basename $0) --auto elive_2.9.12_beta_usb.img" echo -e "Note: this tool can be used to record most of linux distro's in USB too" echo -e "Other options: -s for simulate, -f for fast writing, -c for cleanup (full fill with zeroes first)" exit ;; --auto) is_auto_mode=1 ;; *.img|*.IMG|*.iso|*.ISO|*.img.gz|*.IMG.GZ|*.iso.gz|*.ISO.GZ|*.img.bz2|*.IMG.BZ2|*.iso.bz2|*.ISO.BZ2|*.img.xz|*.IMG.XZ|*.iso.xz|*.ISO.XZ|*.img.lzma|*.IMG.LZMA|*.iso.lzma|*.ISO.LMZA) image_filename="$arg" # verify if [[ -s "$image_filename" ]] ; then is_image_given=1 else el_error "Invalid image given" exit fi # get the name version image_version="$( echo "$image_filename" | sed -e 's|^.*/||g' -e 's|elive_||g' -e 's|_.*$||g' )" if ! echo "$image_version" | grep -qs "^[[:digit:]].*[[:digit:]]$" ; then image_version="$( basename "$image_filename" )" fi # fix paths if [[ "$image_filename" != /* ]] ; then image_filename="$(pwd)/$image_filename" fi # get size image_filename_size="$( du -Ls "$image_filename" | awk '{print $1}' )" el_debug "filename size is ${image_filename_size}" ;; -c|--clean) is_full_rewrite=1 ;; --help) usage exit ;; -f|--fast) is_mode_fast=1 ;; -s|--simulate) is_mode_simulate=1 ;; esac shift done if [[ "$UID" != 0 ]] && ! groups | grep -wqs disk ; then $guitool --error --text="$( eval_gettext "This tool needs to be run with admin privileges. Try from a terminal:" ) sudo $(basename $0)" exit 1 fi mkdir -p "$cache_dir" cd "$cache_dir" # do not run it in the live system # if grep -qs "boot=live" /proc/cmdline ; then # guitool error "$( eval_gettext "This tool is made to work under an installed version of Elive. Install Elive first in order to use it." )" # exit # fi if ! ((is_auto_mode)) ; then if ! guitool question "$( eval_gettext "This tool will overwrite your USB with an ISO image of Elive or any other operating system. After that, you will be able to boot a computer from this USB with that OS. Remember that Elive also has unique features like a special Persistence to save your data among reboots in a very secure way." )" ; then exit fi fi #el_check_translations_required "report" # }}} # select version {{{ if ! ((is_image_given)) ; then #if guitool question "$( eval_gettext "Do you already have a copy of Elive? (If you don't, we'll try to download one.)" )" ; then image_filename="$( $guitool --file-selection --filename="$( xdg-user-dir DOWNLOAD )/" --file-filter="*.img *.IMG *.iso *.ISO" || echo cancel )" if [[ -s "$image_filename" ]] && ! [[ "$image_filename" = "cancel" ]] && ! [[ -z "$image_filename" ]] ; then is_image_given=1 image_version="$( echo "$image_filename" | sed -e 's|^.*/||g' -e 's|elive_||g' -e 's|_.*$||g' )" image_filename_size="$( du -Ls "$image_filename" | awk '{print $1}' )" if ! echo "$image_version" | grep -qs "^[[:digit:]].*[[:digit:]]$" ; then image_version="$( basename "$image_filename" )" guitool info "$( eval_gettext "This doesn't appear to be a normal Elive version, but we will record it anyway." )" fi else unset image_filename unset is_image_given fi #fi if ! ((is_image_given)) ; then # ask for img / iso if guitool question "$( eval_gettext "Do you want to use the IMG version? The IMG version can have persistence with encryption, but the ISO version is more compatible with most devices." )" ; then image_extension="img" else image_extension="iso" fi guitool working_stop guitool working_start image_stable_url="$( lynx -dump "${url_elive}/download/stable/" | grep "${url_elive}/downloads/stable/.*\.${image_extension}" | sed -e 's|^.*www\.|www.|g' | sort -V | tail -1 )" read -r image_stable_url <<< "$image_stable_url" image_stable_version="${image_stable_url%/*}" image_stable_version="${image_stable_version##*/}" image_stable_filename="${image_stable_url##*/}" # topaz version don't supports USB, or at least from this tool if [[ "$image_stable_version" = "2.0" ]] ; then unset image_stable_url image_stable_version image_stable_filename fi image_beta_url="$( lynx -dump "${url_elive}/download/beta/" | grep "${url_elive}/downloads/other/.*\.${image_extension}" | sed -e 's|^.*www\.|www.|g' | sort -V | tail -1 )" # no download available from the website (request-donation-mode set?) if [[ -z "$image_beta_url" ]] ; then image_beta_url="$( lynx -dump "isos.elivecd.org/development/" | grep "/development/elive_.*\.${image_extension}\$" | sed -e 's|^.*http|http|g' | sort -V | tail -1 )" #if [[ -z "$image_beta_url" ]] ; then #image_beta_url="$( lynx -dump "isos.elivecd.org/beta/" | grep "/beta/elive_.*\.${image_extension}\$" | sed -e 's|^.*http|http|g' | sort -V | tail -1 )" #fi fi read -r image_beta_url <<< "$image_beta_url" image_beta_version="${image_beta_url%/*}" image_beta_version="${image_beta_version##*/}" image_beta_filename="${image_beta_url##*/}" if [[ -n "$image_stable_url" ]] && [[ -n "$image_beta_url" ]] ; then # ask version to use if guitool question "$( eval_gettext "Do you want to use the latest Beta version instead of the Stable one? It has more features and/or newer software but is less polished." )" ; then image_url="$image_beta_url" image_version="$image_beta_version" image_filename="$image_beta_filename" image_subdir="development" else image_url="$image_stable_url" image_version="$image_stable_version" image_filename="$image_stable_filename" image_subdir="stable" fi else if [[ -n "$image_beta_url" ]] ; then # only beta available if [[ -z "$image_stable_url" ]] ; then image_url="$image_beta_url" image_version="$image_beta_version" image_filename="$image_beta_filename" image_subdir="development" fi # only stable available if [[ -z "$image_beta_url" ]] ; then image_url="$image_stable_url" image_version="$image_stable_version" image_filename="$image_stable_filename" image_subdir="stable" fi fi fi # checks if [[ -z "$image_url" ]] ; then #guitool error "$( eval_gettext "No versions of Elive for USB available found from the website." )" guitool info "$( eval_gettext "First, download the version of Elive that you want to use. Then insert the downloaded file into this tool in order to write it to your USB memory" )" #el_error "no versions found on the website" exit 0 fi # get redirector, specially needed for zsync to work guitool working_start _image_url="$( LC_ALL=C timeout 10 wget --verbose "$image_url" -O /dev/null 2>&1 | grep -i "Location:.*following" | sed -e 's|^Location: ||g' -e 's|\[follow.*$||g' )" if [[ "$_image_url" = http* ]] ; then image_url="$_image_url" read -r image_url <<< "$image_url" fi fi fi # - select version }}} # download images {{{ if ! ((is_image_given)) ; then mkdir -p "$cache_dir/downloads/${image_subdir}/${image_extension}" cd "$cache_dir/downloads/${image_subdir}/${image_extension}" image_md5="${image_filename%.*}.md5" rm -f "$image_md5" # download the md5 case "$image_extension" in img) wget $wgetopts --quiet "${image_url%/*}/${image_filename%.img}.md5" ;; iso) wget $wgetopts --quiet "${image_url%/*}/${image_filename%.iso}.md5" ;; esac if [[ -s "$image_filename" ]] ; then # download ready to use # TODO: check md5 to know that is valid guitool working_start if md5sum -c "${image_md5}" ; then is_checksum_ok=1 else is_redownload_wanted=1 fi guitool working_stop else image_old="$( ls -1 *.img 2>/dev/null | sort -V | tail -1 )" # re-use with zsync if [[ -s "$image_old" ]] ; then # use zsync rm -rf old mkdir -p old mv -f elive*${image_extension} old/ # TODO: test if works with the temporal url zsync -i "old/${image_old}" "${image_url}.zsync" 1>"$TMP_PROGRESS_WORKING_f" 2>"$TMP_ERROR_LOGS_f" & pid="$!" sleep 2 local message_downloading message_downloading="$( printf "$( eval_gettext "Synchronizing to Elive (%s), please be patient..." )" "$image_version" )" local message_comparing message_comparing="$( printf "$( eval_gettext "Searching for particles in the universe to recycle, to save you around 90 percent of the needed download!" )" "" )" while kill -0 "$pid" 2>/dev/null do progress="$( cat "$TMP_PROGRESS_WORKING_f" | grep % | tail -1 | tr ' ' '\n' | grep % | sed -e 's|%||g' -e 's|\..*$||g' | tail -1 )" if [[ "$progress" = 100 ]] ; then #if ! shuf -i 30-70 -n 1 2>/dev/null ; then #echo "50" #fi echo "# $message_comparing" else #echo "$progress" echo "# ${message_downloading}: ${progress} %" fi sleep 5 done | $guitool --progress --pulsate --auto-close --text="$( eval_gettext "$message_downloading" )" || exit 1 # cleanups rm -f rcksum-* # errors? if grep -qs "No space left" "$TMP_ERROR_LOGS_f" "${TMP_PROGRESS_WORKING_f}" ; then guitool error "$( eval_gettext "No space left on the device. Please free some space on your disk and then run this tool again." )" rm -f "$image_filename" cd rm -rf "$cache_dir" exit 1 fi # TODO: add a function to verify signature #guitool working_start if md5sum -c "${image_md5}" ; then is_checksum_ok=1 rm -rf old/ else guitool error "$( eval_gettext "The integrity verification doesn't match. Run this tool again to try with a new download." )" el_error "md5 verification failed" exit 1 fi guitool working_stop fi fi # we don't have it, nor zsynced, download it if ! [[ -s "$image_filename" ]] || ((is_redownload_wanted)) ; then LC_ALL=C wget $wgetopts "$image_url" 1>"$TMP_PROGRESS_WORKING_f" 2>"$TMP_ERROR_LOGS_f" & pid="$!" sleep 2 local message_downloading message_downloading="$( printf "$( eval_gettext "Downloading Elive (%s). Please be patient..." )" "$image_version" )" while kill -0 "$pid" 2>/dev/null do progress="$( cat "$TMP_PROGRESS_WORKING_f" | grep % | tail -1 | tr ' ' '\n' | grep % | sed -e 's|%||g' -e 's|\..*$||g' | tail -1 )" echo "$progress" sleep 5 done | $guitool --progress --auto-close --text="$( eval_gettext "$message_downloading" )" || exit 1 # errors? if grep -qs "No space left" "$TMP_ERROR_LOGS_f" "${TMP_PROGRESS_WORKING_f}" ; then guitool error "$( eval_gettext "No space left on the device. Please free some space on your disk and then run this tool again." )" rm -f "$image_filename" cd rm -rf "$cache_dir" exit 1 fi # TODO: guitool working_start if md5sum -c "${image_md5}" ; then is_checksum_ok=1 else rm -f "$image_filename" fi guitool working_stop fi if [[ -s "$image_filename" ]] && ! ((is_checksum_ok)) ; then guitool working_start if md5sum -c "${image_md5}" ; then is_checksum_ok=1 else rm -f "$image_filename" guitool error "$( eval_gettext "The integrity verification doesn't match. Run this tool again to try with a new download." )" el_error "md5 verification failed" exit 1 fi guitool working_stop fi if ! ((is_checksum_ok)) ; then guitool error "$( eval_gettext "The integrity verification doesn't look to be correct." )" el_error "integrity has not been verified" exit 1 fi fi # verify md5 # - download images }}} # select device to record Elive {{{ generate_menu_selector # not inserted ? do it! if [[ "${#menu[@]}" -lt 3 ]] ; then guitool info "$( eval_gettext "Please insert your USB media to record before continuing." )" generate_menu_selector if [[ "${#menu[@]}" -lt 3 ]] ; then sleep 6 generate_menu_selector fi fi if [[ "${#menu[@]}" -lt 3 ]] ; then #guitool error "$( eval_gettext "No USB thumb drives were found." )" if guitool question "$( eval_gettext "No USB thumb drives found. Do you want to show all the available hard disks? (Use this option with caution, you can destroy all your computer's data if you select the wrong disk!)" )" ; then generate_menu_selector "include_hd" if [[ "${#menu[@]}" -lt 3 ]] ; then guitool error "$( eval_gettext "No USB thumb drives were found." )" exit 1 fi is_hd_included=1 else exit 1 fi fi if ((is_auto_mode)) ; then if [[ "${#menu[@]}" -eq 3 ]] ; then if [[ -b "${menu[0]}" ]] ; then image_device_size="$( EL_DEBUG=0 partitions-list --show-disks --show-raw --show-only="${menu[0]}" | awk -v FS="::" '{print $6 }' )" if [[ "$image_device_size" -gt 3500000000 ]] && [[ "$image_device_size" -lt 75000000000 ]] ; then image_device="${menu[0]}" # confirm needed if ! el_confirm "Detected USB as ${image_device}, record on this one?" ; then unset image_device fi else el_info "Device '${menu[0]}' named '${menu[2]}' has a size of '${image_device_size}' which is not common in USB sticks, switching to manual mode..." fi fi fi fi local message_select_usb message_select_usb="$( printf "$( eval_gettext "Select the USB device where to record the system. All the data and partitions inside it will be permanently deleted!" )" "" )" if ! [[ -n "$image_device" ]] || ! [[ -b "$image_device" ]] ; then image_device="$( $guitool --list --height=300 --width=600 --text="$message_select_usb" --column="$( eval_gettext "Device" )" --column="$( eval_gettext "Size" )" --column="$( eval_gettext "Details" )" "${menu[@]}" || echo cancel )" if [[ "$image_device" = "cancel" ]] || [[ -z "$image_device" ]] || [[ ! -b "$image_device" ]] ; then if guitool question "$( eval_gettext "You have not selected any device to use. Do you want to exit?" )" ; then exit 1 else generate_menu_selector image_device="$( $guitool --list --height=300 --width=600 --text="$message_select_usb" --column="$( eval_gettext "Device" )" --column="$( eval_gettext "Size" )" --column="$( eval_gettext "Details" )" "${menu[@]}" || echo cancel )" if [[ "$image_device" = "cancel" ]] || [[ -z "$image_device" ]] || [[ ! -b "$image_device" ]] ; then el_error "wrong device selected: $image_device" exit 1 fi fi fi fi # async mode for faster devices {{{ if [[ "$image_device" = "/dev/mmcblk"* ]] || ((is_mode_fast)) ; then # this is external HD's, or like an external NVME/SSD from external USB convertor # update: seems like all USBs shows like that, so don't use it #if udevadm info --query=property --name="$image_device" | grep -qs "ID_TYPE=disk" ; then el_debug "disabling dd in sync mode (progress bar will be not very precise)" local message_doing_magic_extra message_magic_extra="$( printf "$( eval_gettext "Note: due to optimizations, the progress bar will appear slower in the end." )" "" )" unset opts_dd_sync #fi fi # - async mode for faster devices }}} # get the size of the device if [[ -z "$image_device_size" ]] ; then image_device_size="$( EL_DEBUG=0 partitions-list --show-disks --show-raw --show-only="$image_device" | awk -v FS="::" '{print $6 }' )" fi # - select device to record Elive }}} # record Elive to device {{{ if [[ -s "$image_filename" ]] && [[ -b "$image_device" ]] ; then local message_recording message_recording="$( printf "$( eval_gettext "Recording Elive %s onto your %s device" )" "$image_version" "$image_device" )" if ! echo "$image_version" | grep -qs "^[[:digit:]].*[[:digit:]]$" ; then message_recording="$( printf "$( eval_gettext "Recording %s onto your %s device" )" "${image_version}" "$image_device" )" fi # check if size is enough if [[ -n "$image_device_size" ]] && [[ -n "$image_filename_size" ]] && [[ "${image_device_size}" -lt "${image_filename_size}000" ]] ; then #el_warning "temporal report: USB is smaller than image to record: image is '${image_filename_size}' and device is '${image_device_size}'. " if ! guitool question "$( eval_gettext "The detected size of your USB media appears to be smaller than the size of the image you want to record to it. This may result in a corrupted system. Do you want to continue anyway?" )" ; then exit fi fi # recyclate persistence partition? # TODO: # ask # cryptsetup support, detect too buf="$( EL_DEBUG=0 partitions-list --show-all --show-disks --show-raw | awk -v FS="::" '{if ($3 != "swap") print $0}' | grep "^${image_device}" )" if echo "$buf" | grep -qsE "(persistence|crypto_LUKS)" ; then if echo "$buf" | grep -qsi "Live_BOOT" ; then if echo "$buf" | grep -qsi "Live_OS" ; then persistence_partition="$( EL_DEBUG=0 partitions-list --show-all 2>/dev/null | grep -E "^${image_device}.*::(persistence|crypto_LUKS)::" | awk -v FS="::" '{print $1}' | tail -1 )" is_persistence_included=1 fi fi fi if ((is_persistence_included)) ; then if ((is_auto_mode)) ; then if ! guitool question "$( eval_gettext "You have a persistence partition in your USB, proceeding will delete it. Are you sure to continue?" )" ; then exit 1 fi else if ! guitool question "$( eval_gettext "You seem to have a Persistence partition included on this USB, if you continue you will lose all the data on it, like possible users and files. Continue anyway?" )" ; then exit fi fi fi if ((is_auto_mode)) ; then el_info "$message_recording" else guitool working_stop fi # checks {{{ if [[ -z "$image_filename_size" ]] ;then image_filename_size="$( du -Ls "$image_filename" | awk '{print $1}' )" fi if ! [[ -n "$image_filename_size" ]] ; then el_error "image given '${image_filename}' has no size?" exit 1 fi # }}} case "$image_filename" in *".gz"|*".GZ") command_cat="zcat" ;; *".bz2"|*".BZ2") command_cat="bzcat" ;; *".lzma"|*".LZMA") command_cat="lzcat" ;; *".xz"|*".XZ") command_cat="xzcat" ;; *) command_cat="cat" ;; esac # add a 10% to the real size to never show 100 until we really finished image_filename_size="$( echo "(${image_filename_size} / 100) * 110" | bc -l | sed -e 's|\..*$||g' )" #el_debug "Running command: $command_cat \"$image_filename\" | pv -n -W -s \"${image_filename_size}k\" 2>\"${TMP_PROGRESS_WORKING_f}\" | dd of=\"$image_device\" bs=4M oflag=dsync" el_debug "Running command: $command_cat \"$image_filename\" | pv -i 2 -L 600M -n -W -s \"${image_filename_size}k\" 2>\"${TMP_PROGRESS_WORKING_f}\" | dd of=\"$image_device\" bs=8M 1>/dev/null 2>\"$TMP_ERROR_LOGS_f\" $opts_dd_sync " # for debug only (with no real writing): #if zcat "$image_filename" | pv -n -W -s "${image_filename_size}k" 2>"${TMP_PROGRESS_WORKING_f}" | dd of="/dev/null" bs=4M oflag=dsync ; then #if zcat "$image_filename" | pv -i 2 -L 600M -n -W -s "${image_filename_size}k" 2>"${TMP_PROGRESS_WORKING_f}" | dd of="/dev/null" bs=8M ; then # confirm write if ((is_hd_included)) && ! guitool question "$( eval_gettext "You selected to write to a hard disk instead of a USB thumb drive. YOU MAY LOSE DATA if you selected the wrong one. Do you want to continue? This disk is going to be erased entirely! :" ) ${image_device}" ; then exit 1 fi if ((is_mode_simulate)) ; then el_info "simulated recording to the disk '${image_device}' " for i in $(seq 10) do echo "${i}0" >> ${TMP_PROGRESS_WORKING_f} LC_ALL=C sleep 0.3 done else # full write zeroes if ((is_full_rewrite)) ; then guitool working_start "$( eval_gettext "Please be patient, writing an entire USB with zeros is a very slow task. This operation will repair or improve your USB media making it work more reliably." )" dd if=/dev/zero of="$image_device" bs=8M 2>"$TMP_ERROR_LOGS_f" sync guitool working_stop fi # show gui guitool working_write_progress # first: erase headers to clean trashy bytes echo "1" >> "$TMP_PROGRESS_WORKING_f" dd if=/dev/zero of="$image_device" bs=8M count=32 2>/dev/null # record the system #if $command_cat "$image_filename" | pv -n -W -s "${image_filename_size}k" 2>"${TMP_PROGRESS_WORKING_f}" | dd of="$image_device" bs=4M oflag=dsync 1>/dev/null 2>"$TMP_ERROR_LOGS_f" ; then if $command_cat "$image_filename" | pv -i 2 -L 600M -n -W -s "${image_filename_size}k" 2>"${TMP_PROGRESS_WORKING_f}" | dd of="$image_device" bs=8M $opts_dd_sync 1>/dev/null 2>"$TMP_ERROR_LOGS_f" ; then el_debug="Last percent wrote speculated as:\n$( tail -n 4 "${TMP_PROGRESS_WORKING_f}" )" sync LC_ALL=C sleep 0.4 echo "100" >> "$TMP_PROGRESS_WORKING_f" else is_record_failed=1 fi sync fi guitool working_stop # show possible errors found if ((is_record_failed)) ; then el_error "dd was unable to write. Image size '${image_filename_size}', Image file '${image_filename}', Device '${image_device}', Device size '${image_device_size}' \nlogs: $(cat "$TMP_ERROR_LOGS_f" )\n$(dmesg | grep -i error | tail -4)\Partitions:\n$( EL_DEBUG=0 partitions-list --show-all --show-disks --show-raw )" guitool error "$( eval_gettext "Error writing to the USB" ). $( eval_gettext "Failed to copy all the content, is your USB too small for the ISO? This error can be also caused by a bad quality USB. We recommend only using good quality devices, for example, the SanDisk Ultra Flair 32GB, which is cheap and fast enough to have the Persistence feature work well. You could also try running this tool from a terminal with the option to perform a forced full cleansing of your device, like:" ) $(basename "$0" ) --clean" exit 1 fi fi local message_congratulations message_congratulations="$( printf "$( eval_gettext "Congratulations, your USB thumb drive is ready to be used on any computer! Remember to boot by pressing the special key on the computer to select the boot device and then select the USB thumb drive. You may need to change some BIOS settings too. You can find detailed information about this on Elive's website or our forums." )" "" )" if ((is_auto_mode)) ; then el_info "$message_congratulations" else guitool info "$message_congratulations" fi # }}} # delete cache if low space {{{ free_space="$( LC_ALL=C df -BM | awk '{if ($6 == "/home") print $4}' | sed -e 's|M$||g' | tail -1 )" if [[ -z "$free_space" ]] ; then free_space="$( LC_ALL=C df -BM | awk '{if ($6 == "/") print $4}' | sed -e 's|M$||g' | tail -1 )" fi if echo "$free_space" | grep -qs "[[:digit:]]" ; then if [[ "$free_space" -lt 40000 ]] ; then cd rm -rf "$cache_dir" fi fi # - delete images if low space }}} # notify if ((is_interactive)) ; then echo -e "\a" fi } # # MAIN # main "$@" # vim: set foldmethod=marker :