#!/bin/bash # piscal manager script usage="$(basename "$0") [-h] -d directory_name [-s|-c|-k] -p photosynthetic_type -u notify_url -t where: -h show this help text -d working directory name -s start job -c cleanup directory -k kill job -u url to notify on completion -p photosynthetic type -t suppress storage copy" # Initialize variables: # http://stackoverflow.com/a/246128/99492 base_directory="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" directory_name="" photosynthetic_type="C3_photosynthesis_leafweb" #default task="get_status" # default task input_directory_name="input" pid_filename="piscal.pid" stderr_filename="piscal.err" cleaned_input_directory_name="output/clninput" output_directory_name="output/fitresult/touser" nottouser_directory_name="output/fitresult/nottouser" launcher="$base_directory/piscal_launcher.sh" # http://stackoverflow.com/a/14203146/99492 # http://wiki.bash-hackers.org/howto/getopts_tutorial while getopts "hd:f:p:stcku:" opt; do #echo "$opt = $OPTARG" case "$opt" in h ) echo "$usage" exit ;; d ) directory_name=$OPTARG ;; p ) photosynthetic_type=$OPTARG ;; s ) task="launch" ;; t ) suppress_storage_copy=true ;; c ) task="cleanup" ;; k ) task="kill" ;; u ) notify_url=$OPTARG ;; \?) printf "illegal option: -%s\n" "$OPTARG" >&2 echo "$usage" >&2 exit 1 ;; esac done ## Examine directories if [ -z "$directory_name" ]; then echo "directory name required (-d)" exit 1 fi candidate_base_directory="$base_directory/$directory_name" # Resolve the real working directory by probing for an input/ directory. # This makes the job orchestration resilient when containers create jobs under # a different root (e.g. $HOME/LeafWeb) than where the manager scripts live. working_directory="" for candidate in \ "$candidate_base_directory" \ "$HOME/LeafWeb/$directory_name" \ "$HOME/$directory_name" \ "$PWD/$directory_name" \ "/srv/$directory_name" do if [ -d "$candidate/$input_directory_name" ] && [ -w "$candidate" ]; then working_directory="$candidate" break fi done # If no candidates match, fall back to the historical behavior so errors remain explainable. if [ -z "$working_directory" ]; then working_directory="$candidate_base_directory" fi input_directory="$working_directory/$input_directory_name" pid_path="$working_directory/$pid_filename" # Preserve fail-loud behavior for launch and status operations. # Cleanup/kill are handled idempotently further below. if [ "$task" = "launch" ] || [ "$task" = "get_status" ] || [ "$task" = "get_status_cleaned_input" ]; then if [ ! -d "$working_directory" ]; then echo "working directory $working_directory not found" exit 1 fi if [ ! -w "$working_directory" ]; then echo "working directory $working_directory is not writable" exit 1 fi if [ ! -d "$input_directory" ]; then echo "input directory $input_directory not found" exit 1 fi fi ## Process task if [ "$task" = "launch" ]; then # verify process isn't running yet pid="$(head -n 1 "$pid_path" 2>/dev/null)" # if the pid exists, check the process status using ps if [ -n "$pid" ] && ps -p "$pid" &>/dev/null; then # if it is in ps, then it's still running echo still running exit 1 fi piscal_config_file="$working_directory"/piscal.cfg # write config file for piscal echo $photosynthetic_type > "$piscal_config_file" find "$input_directory" -maxdepth 1 -type f\ -printf '%P\n'\ >> "$piscal_config_file" command="$launcher -d $working_directory -f piscal.cfg" if [ "$suppress_storage_copy" = true ]; then command="$command -t" fi if [ -n "$notify_url" ]; then command="$command -u $notify_url" fi # launch it, sending error output to file nohup ${command} > "$working_directory/$stderr_filename" 2>&1 & # write the PID to a temp file to check for completion later echo $! > "$pid_path" echo started elif [ "$task" = "get_status" ] || [ "$task" = "get_status_cleaned_input" ]; then # if the pid doesn't exist, then process hasn't started if [ ! -f "$pid_path" ]; then echo "not started" exit fi pid="$(head -n 1 "$pid_path" 2>/dev/null)" if [ -z "$pid" ]; then echo "not started" exit fi # if the pid exists, check the process status using ps if ps -p "$pid" > /dev/null; then # if it is in ps, then it's still running echo running else # otherwise, it is complete, check for runtime errors if [ -s "$working_directory/$stderr_filename" ]; then cat "$working_directory/$stderr_filename" exit 1 fi output_directory="$working_directory/$output_directory_name" if [ ! -d "$output_directory" ]; then echo "output directory $output_directory not found" exit 1 fi echo complete if [ "$task" = "get_status" ]; then echo "#touser" find "$output_directory"/* 2>/dev/null cleaned_input_directory="$working_directory/$cleaned_input_directory_name" echo "#clninput" find "$cleaned_input_directory"/* 2>/dev/null nottouser_directory="$working_directory/$nottouser_directory_name" echo "#nottouser" find "$nottouser_directory"/* 2>/dev/null exit 0 fi fi elif [ "$task" = "cleanup" ]; then # Idempotent cleanup: safe after partial failures. if [ ! -d "$working_directory" ]; then exit 0 fi if [ ! -f "$pid_path" ]; then exit 0 fi pid="$(head -n 1 "$pid_path" 2>/dev/null)" # if the pid exists, check the process status using ps if [ -n "$pid" ] && ps -p "$pid" > /dev/null; then # if it is in ps, then it's still running echo still running exit 1 fi rm -rf "$working_directory" elif [ "$task" = "kill" ]; then # Idempotent kill: safe after partial failures. if [ ! -d "$working_directory" ]; then exit 0 fi if [ ! -f "$pid_path" ]; then exit 0 fi pid="$(head -n 1 "$pid_path" 2>/dev/null)" if [ -z "$pid" ]; then exit 0 fi # if the pid exists, check the process status using ps if ps -p "$pid" > /dev/null; then # if it is in ps, then it's still running kill "$pid" fi echo killed fi