#!/usr/bin/env bash working_dir="$(dirname "$(realpath $0)")" conf_file="${working_dir}/conf/mail_to_listadmins.conf" _date="$(date +%Y-%m-%d-%H%M)" log_dir="${working_dir}/log" log_file="${log_dir}/mail_to_listadmins.${_date}.log" schleuder_conf_file="/etc/schleuder/schleuder.yml" LOCK_DIR="$(mktemp -d)" err_msg="${LOCK_DIR}/err_msg.log" #----------------------------- # Some variables definitions #----------------------------- declare -a schleuder_domains_arr declare -a schleuder_lists_arr declare -a lists_done declare -a lists_failed declare -a lists_warn #----------------------------- # Base Function(s) #----------------------------- clean_up() { # Perform program exit housekeeping rm -rf $LOCK_DIR exit $1 } echo_log() { echo "$*" >> "$log_file" } echo_log_done() { echo "[ done ] $*" >> "$log_file" } echo_log_failed() { echo "[ Error ] $*" >> "$log_file" } echo_log_skipped() { echo "[ skipped ] $*" >> "$log_file" } echononl(){ if $terminal ; then echo X\\c > /tmp/shprompt$$ if [ `wc -c /tmp/shprompt$$ | awk '{print $1}'` -eq 1 ]; then echo -e -n "$*\\c" 1>&2 else echo -e -n "$*" 1>&2 fi rm /tmp/shprompt$$ else echo -n "$*" fi } fatal(){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mFatal\033[m ]: $*" echo "" echo -e " \033[31m\033[1mScript was interupted\033[m!" else echo " [ Fatal ]: $*" echo "" echo " Script was terminated...." fi echo "" clean_up 1 } error (){ echo "" if $terminal ; then echo -e " [ \033[31m\033[1mError\033[m ]: $*" else echo " [ Error ]: $*" fi echo "" } warn (){ echo "" if $terminal ; then echo -e " [ \033[33m\033[1mWarning\033[m ]: $*" else echo " [ Warning ]: $*" fi echo "" } info (){ echo "" if $terminal ; then echo -e " [ \033[32m\033[1mInfo\033[m ]: $*" else echo " [ Info ]: $*" fi echo "" } echo_done() { if $terminal ; then echo -e "\033[75G[ \033[32mdone\033[m ]" else echo " [ done ]" fi } echo_failed(){ if $terminal ; then echo -e "\033[75G[ \033[1;31mfailed\033[m ]" else echo " [ failed ]" fi } echo_skipped() { if $terminal ; then echo -e "\033[75G[ \033[33m\033[1mskipped\033[m ]" else echo " [ skipped ]" fi } function usage () { if [ -n "$1" ];then echo -e "\nError: $1" fi echo -e " Usage: \033[1m$(basename $0) [-h] [<-s email-address>] [-f ]\033[m $(basename $0) sends a message, reading from a given file, to all list administrators of all supported schleuder3 lists. The given message file (and within the containing text) must be UTF8 encoded. In the message, you can use the parameter string '@LIST@' which will be replaced by the full qualified list address for the appropriate list. \033[1mNotice\033[m The keyring of the user invoking this script (for now only user 'root' is possible) must contain the private key for the given sender address. The script will ask you for the concerning passwort. The message file may contain string \033[1m@LIST@\033[m which will be replaced by the list address. Parameters not given on the commandline will be interactivly ask for. Optinal Control Options: -h print that help text -b Subject subject name of the e-mail. -p Password Password for the secret key of the sender address. Its \033[1mhighly not recommend\033[m to use this parameter, because the password is prompted out and maybe also saved in the shell's history file. If omitting this option, the script will ask for the password. The password will not be prompted at the commandline. -s Sender Adrress name of the sender e-mail address. -f Message File full qualified path tio the file containing the message Example: - Normal use at the command line: ./$(basename $0) -b \"Listsoftware has changed\" -f /root/msg_listadmin.txt -s support@so36.net - Use as cronjob: echo 'YES' | $(realpath "$0") -b \"Info E-Mail\" -p -s \"support@so36.net\" -f \"/root/bin/schleuder3/messages/msg_listadmins.txt\" \033[1mNote\033[m: All options must be present, at the configuration file or at the command line. " clean_up 2 } # - See https://gist.github.com/epiloque/8cf512c6d64641bde388 parse_yaml() { local prefix=$2 local s local w local fs s='[[:space:]]*' w='[a-zA-Z0-9_]*' #fs="$(echo @|tr @ '\034')" fs="$(echo @|tr @ '\034'|tr -d '\015')" sed -ne "s|^\($s\)\($w\)$s:$s\"\(.*\)\"$s\$|\1$fs\2$fs\3|p" \ -e "s|^\($s\)\($w\)$s[:-]$s\(.*\)$s\$|\1$fs\2$fs\3|p" "$1" | awk -F"$fs" '{ indent = length($1)/2; vname[indent] = $2; for (i in vname) {if (i > indent) {delete vname[i]}} if (length($3) > 0) { vn=""; for (i=0; i "$err_msg" 2>&1; then error "$(cat "$err_msg")" fatal "(Schleuder) User '$schleuder_user' does not exists" fi #----------------------------- # Read default values from script configuration file #----------------------------- SENDER_ADDRESS="" MESSAGE_FILE="" SUBJECT="" echo "" echononl " Loading default Configuration values from $(basename ${conf_file}).." if [[ ! -f "$conf_file" ]]; then echo_skipped else source "${conf_file}" > $err_msg 2>&1 if [[ $? -eq 0 ]]; then echo_done else echo_failed fatal "$(cat $err_msg)" fi fi if $terminal ; then [[ -n "$SENDER_ADDRESS" ]] && default_sender_address="$SENDER_ADDRESS" [[ -n "$MESSAGE_FILE" ]] && default_orig_msg_file="$MESSAGE_FILE" [[ -n "$SUBJECT" ]] && default_subject="$SUBJECT" else [[ -n "$SENDER_ADDRESS" ]] && sender_address="$SENDER_ADDRESS" [[ -n "$MESSAGE_FILE" ]] && orig_msg_file="$MESSAGE_FILE" [[ -n "$SUBJECT" ]] && subject="$SUBJECT" fi #----------------------------- # Read Commandline Parameters #----------------------------- #subject="" #sender_address="" #gpg_pw="" #orig_msg_file="" while getopts b:hf:p:s: opt ; do case $opt in b) subject="$OPTARG" ;; s) sender_address="$OPTARG" ;; p) gpg_pw="$OPTARG" ;; f) orig_msg_file="$OPTARG" ;; h) usage ;; *) usage esac done if $terminal && [[ -z "$orig_msg_file" ]] ; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Enter full qualified file name containing the message" echo "you want to send to the list admins." echo "" orig_msg_file="" if [[ -n "$default_orig_msg_file" ]]; then echononl "Message file [$default_orig_msg_file]: " read orig_msg_file if [[ -z "$orig_msg_file" ]]; then orig_msg_file="$default_orig_msg_file" fi else while [[ -z "$orig_msg_file" ]] ; do echononl "Message file: " read orig_msg_file if [[ -z "$orig_msg_file" ]]; then echo -e "\n \033[33m\033[1mMessage file is required!\033[m\n" fi done fi fi if [[ ! -f "$orig_msg_file" ]]; then if $terminal ; then fatal "Message file \033[1m$orig_msg_file\033[m not found!" else fatal "Message file $orig_msg_file not found!" fi fi if $terminal && [[ -z "$subject" ]]; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Enter the subject for sending the info e-mails." echo "" subject="" if [[ -n "$default_subject" ]]; then echononl "Subject [$default_subject]: " read subject if [[ -z "$subject" ]]; then subject="$default_subject" fi else while [[ -z "$subject" ]] ; do echononl "Subject: " read subject if [[ -z "" ]]; then echo -e "\n \033[33m\033[1mSender e-mail address is required!\033[m\n" fi done fi fi # - To encode a header using this technique you must use this format: # - # - =?charset?encoding?encoded-text?= # - # - The encoding must be either B or Q, these mean base 64 and quoted-printable respectively. # - You can read the RFC 1342 document for more information about how they work. # - subject_encoded="" if echo "$subject" 2> /dev/null | file -i - | grep -i -q utf-8 ; then subject_encoded="=?UTF-8?B?$(echo "$subject" | base64)?=" fi if $terminal && [[ -z "$sender_address" ]] ; then echo "" echo -e "\033[32m--\033[m" echo "" echo "Enter the sender e-mail address you want to use for sending the info e-mails." echo "" sender_address="" if [[ -n "$default_sender_address" ]]; then echononl "Sender e-mail [$default_sender_address]: " read sender_address if [[ -z "$sender_address" ]]; then sender_address="$default_sender_address" fi else while [[ -z "$sender_address" ]] ; do echononl "Sender e-mail: " read sender_address if [[ -z "" ]]; then echo -e "\n \033[33m\033[1mSender e-mail address is required!\033[m\n" fi done fi fi # - Secret Key for $sender_address must be present # - if ! gpg --list-secret-keys "${sender_address}" > /dev/null 2>&1 ; then fatal "No secret key for id \"${sender_address}\" found" fi if $terminal && [[ -z "$gpg_pw" ]]; then _gpg_pw_1="X" _gpg_pw_2="Y" echo "" echo -e "\033[32m--\033[m" echo "" echo "Enter gpg password for ${sender_address}. The password will not" echo "be shown at the command line" echo "" while [[ "$_gpg_pw_1" != "$_gpg_pw_2" ]] ; do echononl "Password: " read -s _gpg_pw_1 echo if [ "X$_gpg_pw_1" = "X" ]; then echo -e "\n \033[33m\033[1mPassword is required!\033[m\n" continue fi echononl "Repeat password: " read -s _gpg_pw_2 echo if [ "X$_gpg_pw_2" = "X" ]; then echo -e "\n \033[33m\033[1mPassword is required!\033[m\n" continue fi if [ "$_gpg_pw_1" != "$_gpg_pw_2" ];then echo -e "\n\t\033[33m\033[1mThe given passwords differs!\033[m\n" else gpg_pw=$_gpg_pw_1 fi done fi gpg-connect-agent reloadagent /bye > /dev/null 2>&1 if ! $(echo "$gpg_pw" | gpg --trust-model=always --pinentry-mode loopback -o /dev/null --local-user $sender_address --passphrase $gpg_pw -as - > /dev/null 2>&1) ; then fatal "Password does not match user id '${sender_address}'!" fi if $terminal ; then clear echo "" echo -e "\033[32m--\033[m" echo "" echo "" echo -e " \033[1;32mSettings for script \033[1m$(basename "$0")\033[m" echo "" echo -e " Sender Address...................: $sender_address" echo -e " E-Mail Subject...................: $subject" if [[ -n "$subject_encoded" ]] ; then echo -e " E-Mail Subject (utf-8 encoded)...: $subject_encoded" fi echo -e " File containing the message......: $orig_msg_file" echo "" echo -e " Message: \n\n\033[1m$(cat "$orig_msg_file")\033[m\n" else echo "" echo "" echo " Settings for script $(basename "$0"):" echo "" echo " Sender Address...................: $sender_address" echo " E-Mail Subject...................: $subject" if [[ -n "$subject_encoded" ]] ; then echo " E-Mail Subject (utf-8 encoded)...: $subject_encoded" fi echo " File containing the message......: $orig_msg_file" echo "" echo " Message: " echo "--" echo "$(cat "$orig_msg_file")" echo "--" fi echo "" if $terminal ; then echo "" echononl "Type upper case \033[1;37mYES\033[m to continue executing this script: " read OK if [[ "$OK" != "YES" ]] ; then fatal "Abort by user request - Answer as not 'YES'" else echo "" echo -e "\033[32m--\033[m" echo "" echo -e " \033[32m----------\033[m" echo -e " \033[1mStarting Script to inform all Listadmins\033[m" echo -e " \033[32m----------\033[m" fi else echo "" echo " ----------" echo " Starting Script to inform all Listadmins" echo " ----------" fi echo_log "# ----------------------------------------------------------------" echo_log "# - Log output of script '$(basename $0)' at $_date" echo_log "# ----------------------------------------------------------------" echo_log "" echo_log "----------" echo_log "Settings for script $(basename "$0"):" echo_log "----------" echo_log "" echo_log "Sender Address...................: $sender_address" echo_log "E-Mail Subject...................: $subject" if [[ -n "$subject_encoded" ]] ; then echo_log "E-Mail Subject (utf-8 encoded)...: $subject_encoded" fi echo_log "File containing the message......: $orig_msg_file" echo_log "" echo_log "Message: " echo_log "--" echo_log "$(cat "$orig_msg_file")" echo_log "--" echo "" echononl " Create log directory" if [[ ! -d "$log_dir" ]]; then mkdir "$log_dir" > /dev/null 2>&1 if [[ $? -ne 0 ]]; then echo_failed else echo_done fi else echo_skipped fi if $terminal ; then echo "" echo "" echo -e " \033[32m----------\033[m" echo -e " \033[1mSummary Lists at $(hostname -f)\033[m" echo -e " \033[32m----------\033[m" echo "" else echo "" echo "" echo " ----------" echo " Summary Lists at $(hostname -f)" echo " ----------" echo "" fi echo_log "" echo_log "" echo_log "----------" echo_log "Summary Lists at $(hostname -f)" echo_log "----------" echo_log # - Get Schleuder domains # - while IFS= read -r -d '' _dir ; do schleuder_domains_arr+=("$(basename "$_dir")") done < <(find "$schleuder_lists_dir" -mindepth 1 -maxdepth 1 -type d -print0) echo_log "Schleuder Domains:" if $terminal ; then echo -e " \033[32m\033[1mSchleuder Domains\033[m:" else echo " Schleuder Domains:" fi for _domain in ${schleuder_domains_arr[@]} ; do echo_log " $_domain" echo " $_domain" done # - Get Schleuder lists # - for _domain in ${schleuder_domains_arr[@]} ; do while IFS= read -r -d '' _list ; do schleuder_lists_arr+=("$_domain:$(basename "$_list")") done < <(find "${schleuder_lists_dir}/${_domain}" -mindepth 1 -maxdepth 1 -type d -print0 | sort -z ) done echo_log "" echo_log "Schleuder Lists" if $terminal ; then echo -e "\n \033[32m\033[1mSchleuder Lists\033[m:" else echo "" echo " Schleuder Lists" fi for _val in "${schleuder_lists_arr[@]}" ; do IFS=':' read -a _val_arr <<< "${_val}" _domain="${_val_arr[0]}" _list="${_val_arr[1]}" if [[ -n "$_last_domain" ]] && [[ "$_last_domain" != "$_domain" ]] ; then echo_log "" echo "" fi echo_log " ${_domain}: ${_list}" echo " ${_domain}: ${_list}" _last_domain="$_domain" done echo_log echo_log "Total number of lists: ${#schleuder_lists_arr[@]}" if $terminal ; then echo -e "\n \033[32m\033[1mTotal number of lists:\033[m: ${#schleuder_lists_arr[@]}" else echo "" echo " Total number of lists: ${#schleuder_lists_arr[@]}" fi if $terminal ; then echo "" echo "" echo -e " \033[32m----------\033[m" echo -e " \033[1mMain part of script: inform list admins\033[m" echo -e " \033[32m----------\033[m" else echo "" echo "" echo " ----------" echo " Main part of script: inform list admins" echo " ----------" fi echo_log "" echo_log "" echo_log "----------" echo_log "Main part of script: inform list admins" echo_log "----------" for _val in "${schleuder_lists_arr[@]}" ; do _missing_key=false _message_prepared=false _message_encrypted=false IFS=':' read -a _val_arr <<< "${_val}" _domain="${_val_arr[0]}" _list="${_val_arr[1]}" msg_file="${LOCK_DIR}/msg-${_list}-${_domain}.txt" msg_file_encrypted="${LOCK_DIR}/msg_encrypted-${_list}-${_domain}.asc" echo_log "" echo_log "${_list}@${_domain}:" if $terminal ; then echo -e "\n \033[32m\033[1m${_domain} - ${_list}\033[m:" else echo "" echo " ${_list}@${_domain}:" fi # - Copy original message in tmp folder ($LOCK_DIR) # - cp "$orig_msg_file" "$msg_file" > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log "$(cat "$err_msg")" error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") continue fi _list_dir="${schleuder_lists_dir}/${_domain}/${_list}" echononl " Add List Key to keyring of 'root'" if ! gpg -k "${_list}@$_domain" > /dev/null 2>&1 ; then gpg --batch --yes --home "$_list_dir" -a --export ${_list}@$_domain 2> /dev/null | gpg --import > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Add List Key to keyring of 'root'" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") continue else echo_log_done "Add List Key to keyring of 'root'" echo_done fi else echo_log_skipped "Add List Key to keyring of 'root'" echo_skipped fi # - Check if gpg key of $sender_address is part of the keyring. Add key if missing # - echononl " Adding key '$sender_address' to keyring.." if ! gpg --home "$_list_dir" -k "$sender_address" > /dev/null 2>&1 ; then gpg --batch --yes -a --export "$sender_address" | gpg --home "$_list_dir" --import > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Adding key '$sender_address' to keyring" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") continue else echo_log_done "Adding key '$sender_address' to keyring" echo_done fi _missing_key=true else echo_log_skipped "Adding key '$sender_address' to keyring" echo_skipped fi echononl " Adjust message file for list.." perl -i -n -p -e "s/\@LIST\@/${_list}\@${_domain}/g" "$msg_file" > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Adjust message file for list" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") else echo_log_done "Adjust message file for list" echo_done _message_prepared=true fi echononl " Encrypt and sign message for list.." if $_message_prepared ; then gpg --trust-model=always --pinentry-mode loopback --passphrase $gpg_pw -a -se -u "${sender_address}" -r "${_list}@$_domain" -o "${msg_file_encrypted}" "${msg_file}" > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Encrypt and sign message for list" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") else echo_log_done "Encrypt and sign message for list" echo_done _message_encrypted=true fi fi if $_message_encrypted ; then boundery_1="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 33 | head -n 1)" boundery_2="$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 33 | head -n 1)" message_id="$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 8 | head -n 1)-$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 4 | head -n 1)-$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 4 | head -n 1)-$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 4 | head -n 1)-$(cat /dev/urandom | tr -dc 'a-f0-9' | fold -w 12 | head -n 1)@so36.net" if [[ -n "$subject_encoded" ]] ; then subject="$subject_encoded" fi cat < "${LOCK_DIR}/e-mail-${_list}-${_domain}.txt" Content-Type: multipart/mixed; boundary="$boundery_1"; protected-headers="v1" From: "so36.NET" To: ${_list}-owner@$_domain Date: $(date +"%a, %d %b %Y %H:%M:%S %z") Message-ID: <$message_id> Subject: $subject --$boundery_1 Content-Type: multipart/mixed; boundary="$boundery_2" --$boundery_2 Content-Type: text/plain; charset=utf-8 Content-Language: de-DE Content-Transfer-Encoding: quoted-printable EOF cat "$msg_file_encrypted" >> "${LOCK_DIR}/e-mail-${_list}-${_domain}.txt" cat <> "${LOCK_DIR}/e-mail-${_list}-${_domain}.txt" --$boundery_2-- --$boundery_1-- EOF echononl " Send info e-mail to list owner .." cat "${LOCK_DIR}/e-mail-${_list}-${_domain}.txt" | su schleuder -c"/usr/bin/schleuder work ${_list}-owner@$_domain" -s /bin/bash > "$err_msg" 2>&1 #cat "${LOCK_DIR}/e-mail-${_list}-${_domain}.txt" | /usr/sbin/sendmail -F "so35.NET" -f support@so36.net ${_list}-owner@$_domain > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Send info e-mail to list owner" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" lists_failed+=("${_list}@$_domain") else echo_log_done "Send info e-mail to list owner" echo_done lists_done+=("${_list}@$_domain") fi fi echononl " Removing key of '$sender_address' from list keyring.." if $_missing_key ; then gpg --batch --yes --home "$_list_dir" --delete-key "$sender_address" > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Removing key of '$sender_address' from list keyring" echo_log "$(cat "$err_msg")" echo_failed warn "$(cat "$err_msg")" lists_warn+=("${_list}@$_domain") else echo_log_done "Removing key of '$sender_address' from list keyring" echo_done fi echononl " Set ownerchip of list keyring back to '${schleuder_user}'" chown ${schleuder_user}:${schleuder_group} $_list_dir/pubring.* > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Set ownerchip of list keyring back to '${schleuder_user}'" echo_log "$(cat "$err_msg")" echo_failed warn "$(cat "$err_msg")" lists_warn+=("${_list}@$_domain") else echo_log_done "Set ownerchip of list keyring back to '${schleuder_user}'" echo_done fi else echo_log_skipped "Removing key of '$sender_address' from list keyring" echo_skipped fi done # - Clear cached gpg password # - echo "" echo "" echo_log "" echo_log "" echononl " Clear cached gpg password.." gpg-connect-agent reloadagent /bye > "$err_msg" 2>&1 if [[ $? -ne 0 ]]; then echo_log_failed "Clear cached gpg password" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" else echo_log_done "Clear cached gpg password" echo_done fi echononl " Archive msg file.." mv "$orig_msg_file" "${orig_msg_file}.${_date}.sent" if [[ $? -ne 0 ]]; then echo_log_failed "Archive msg file" echo_log "$(cat "$err_msg")" echo_failed error "$(cat "$err_msg")" else echo_log_done "Archive msg file" echo_done fi if $terminal ; then echo "" echo "" echo -e " \033[32m----------\033[m" echo -e " \033[1mStatistics \033[m" echo -e " \033[32m----------\033[m" echo "" echo -e " Total number of lists: ${#schleuder_lists_arr[@]}" echo "" echo -e " ${#lists_done[@]} lists successfully informed" echo -e " ${#lists_warn[@]} lists with warnings" echo -e " ${#lists_failed[@]} lists failed" else echo "" echo "" echo " ----------" echo " Statistics" echo " ----------" echo "" echo " Total number of lists: ${#schleuder_lists_arr[@]}" echo "" echo " ${#lists_done[@]} lists successfully informed" echo " ${#lists_warn[@]} lists with warnings" echo " ${#lists_failed[@]} lists failed" fi echo_log "" echo_log "" echo_log "----------" echo_log "Statistics" echo_log "----------" echo_log echo_log "Total number of lists: ${#schleuder_lists_arr[@]}" echo_log echo_log "${#lists_done[@]} lists successfully informed" echo_log "${#lists_warn[@]} lists with warnings" echo_log "${#lists_failed[@]} lists failed" echo_log if [[ ${#lists_failed[@]} -gt 0 ]]; then echo_log "Listadmin(s) not informed:" if $terminal ; then echo -e "\n \033[1mListadmin(s) not informed:\033[m" else echo "" echo " Listadmin(s) not informed:" fi for _list in "${lists_failed[@]}" ; do echo -e " $_list" echo_log " $_list" done echo_log info "See also log file: $log_file" else info "See also script output at log file: $log_file" fi echo "" clean_up 0