#!/bin/sh openfoamVersion="latest" imageBasename="opencfd/openfoam" imageFlavour="-run" scriptVersion="2022-01-10" #------------------------------------------------------------------------------ # ========= | # \\ / F ield | OpenFOAM: The Open Source CFD Toolbox # \\ / O peration | # \\ / A nd | www.openfoam.com # \\/ M anipulation | #------------------------------------------------------------------------------ # Copyright (C) 2016-2022 OpenCFD Ltd. #------------------------------------------------------------------------------ # SPDX-License-Identifier: (GPL-3.0-or-later) # # Script # openfoam-docker # # Description # Run script for openfoam container images # (https://hub.docker.com/u/opencfd) # # Copy/link to automatically specify OpenFOAM version and image 'flavour' # For example, # # ln -s openfoam-docker openfoam2112-run # #------------------------------------------------------------------------------ printHelp() { defaultImage="${imageBasename}${imageFlavour}:${openfoamVersion}" cat<<HELP_HEAD Usage: ${0##*/} [OPTION] [-- application ...] ${0##*/} [OPTION] [command_string] options: -c Shell commands read from the first non-option argument -data=DIR Specify mount dir for container '/data' -dir=DIR Specify mount dir for container '~/' (default: pwd) -<digits> Specify OpenFOAM version (eg, -2112) -base | -dev | -run | -tut | -default Image flavour (default: -run). Not all image flavours are necessarily available. -X | -x X11 forwarding: enable (-X) or disable (-x) -update Update (pull) the image, do not run -dry-run Report the start command, without running -verbose Additional verbosity when starting (FOAM_VERBOSE) HELP_HEAD if [ -n "$1" ] then cat<<'HELP_FULL' -entry=PATH Alternative entry point -image=NAME | -i=NAME Specify image to run -docker Use docker (default) -podman Use podman instead of docker -sudo Prefix container calls with 'sudo' -xhost Allow container access to host network (implies -X) --shm-size=BYTES Size of /dev/shm (eg, --shm-size=4G) HELP_FULL fi cat<<TAIL_OPTIONS -- The end of option processing. An argument of - or / is equivalent to --. -h | -help Display short help and exit -help-full Display full help and exit Run OpenFOAM container image (${defaultImage}) - Simulations are stored on the host system (not the container) - Mounts the current or specified directory (except the HOME directory). Script Version: ${scriptVersion:-[]} Note: The user name within the container is 'openfoam', but matches the user/group id from the host. Requires docker or podman. TAIL_OPTIONS if [ -n "$1" ] then cat<<HELP_FULL Equivalent options: | -dir=DIR | -case=DIR | -case DIR | -home=DIR Example: ${0##*/} -dir=/path/to/simulation or cd /path/to/simulation && ${0##*/} Note: The '-xhost' option can be useful in combination with 'ssh -X', but should be used sparingly since it permits the container full access to network communication (potentially insecure). HELP_FULL fi cat<<'FOOTER' Note: Different OpenFOAM components and modules may be present (or missing) on any particular container image. For example, source code, tutorials, in-situ visualization, paraview plugins, external linear-solver interfaces etc. For more information: www.openfoam.com FOOTER exit 0 # A clean exit } # Report error and exit die() { exec 1>&2 echo echo "Error encountered:" while [ "$#" -ge 1 ]; do echo " $1"; shift; done echo echo "See '${0##*/} -help' for usage" echo exit 1 } #------------------------------------------------------------------------------ # Constants - user name/locations MUST correspond to the image assets toolChain=docker container_home='/home/openfoam' # Home for container user container_tmphome='/tmp/.home.openfoam' # Fake home for container user #------------------------------------------------------------------------------ # Select 'podman' toolchain if mentioned in script name: case "${0##*/}" in (*-podman*) toolChain=podman;; esac # Get openfoam version and/or image flavour from script name # "/path/openfoam{VERSION}" # "/path/openfoam{VERSION}-{FLAVOUR}" imageTag="$(echo "${0##*/}" | sed -ne 's/openfoam\([1-9][0-9]*\).*/\1/ip')" # Version: if [ -n "$imageTag" ] then openfoamVersion="$imageTag" fi # Flavour: imageTag="-${0##*-}" case "$imageTag" in (-base) unset imageFlavour ;; (-run | -default) imageFlavour="$imageTag" ;; (-dev*) imageFlavour="-dev" ;; (-tut*) imageFlavour="-tutorials" ;; esac #------------------------------------------------------------------------------ # Parse options unset image sudo unset mount1Dir mount2Dir unset optDryrun optEntrypoint optShellCommand optUpdate optVerbose optShmSize unset optX11Forwarding while [ "$#" -gt 0 ] do case "$1" in ('') ;; (- | -- | /) shift break # Stop option parsing ;; # OpenFOAM versions (eg, -2112, -2106, -2012, etc) (-[1-9]*) openfoamVersion="${1#*-}" ;; (-v[1-9]*) openfoamVersion="${1#*-v}" ;; # Image flavours (-base) unset imageFlavour ;; (-run) imageFlavour="-run" ;; (-def*) imageFlavour="-default" ;; (-dev*) imageFlavour="-dev" ;; (-tut*) imageFlavour="-tutorials" ;; (-c) # Shell command optShellCommand="-c" ;; (-help-f*) # Full help printHelp -full ;; (-h | -help* | --help*) # Short help printHelp ;; (-docker | -podman) toolChain="${1#*-}" ;; (-sudo) # Use sudo sudo="sudo" ;; (--shm-size=*) optShmSize="${1#*=}" ;; (-case) # OpenFOAM-style '-case' for specfying the HOME mount [ "$#" -ge 2 ] || die "'$1' option requires an argument" shift mount1Dir="$1" ;; # Various ways to specify the HOME mount (-case=* | -dir=* | -home=*) mount1Dir="${1#*=}" ;; # Additional DATA mount (-data=*) mount2Dir="${1#*=}" ;; (-entry=*) # Alternative entrypoint optEntrypoint="${1#*=}" ;; (-i=* | -image=*) # Alternative image name image="${1#*=}" ;; (-update | -upgrade) # Also allow 'upgrade' (for Ubunutu people) optUpdate=true ;; (-dry-run | -dryrun) optDryrun=true ;; (-verbose) optVerbose=true ;; (-X) # Enable X11 forwarding : "${optX11Forwarding:=X}" ;; (-x) # Disable X11 forwarding unset optX11Forwarding ;; (-xhost) # Any host X11 forwarding optX11Forwarding="host" ;; (-*) die "Invalid option '$1'" ;; (*) if [ -n "$optShellCommand" ] then break else die "Unexpected argument '$1'" fi ;; esac shift done if [ -z "$image" ] then image="${imageBasename}${imageFlavour}:${openfoamVersion}" fi if [ -n "$optUpdate" ] then if [ -n "$optDryrun" ] then runPrefix="echo" echo "(dry-run)" 1>&2 echo 1>&2 else runPrefix="$sudo" echo "Update image: $image" 1>&2 fi $runPrefix ${toolChain:?} pull "$image" exitCode="$?" echo 1>&2 echo "Done" 1>&2 exit "$exitCode" fi if [ -n "$optShellCommand" ] && [ "$#" -eq 0 ] then die "-c: option requires an argument" fi #------------------------------------------------------------------------------ # Sanity and setup guest_uid="$(id -u 2>/dev/null)" guest_gid="$(id -g 2>/dev/null)" [ -n "$guest_uid" ] || die "Cannot determine current user id" [ -n "$guest_gid" ] || die "Cannot determine current group id" # Mount directory (mandatory) if [ -z "$mount1Dir" ] then mount1Dir="$(pwd -P)" # Default is current directory elif [ -d "$mount1Dir" ] then mount1Dir="$(cd "$mount1Dir" && pwd -P)" else die "No such mount directory: $mount1Dir" fi if [ "$mount1Dir" = "$(cd "$HOME" && pwd -P)" ] then die "Cannot use home directory as the mount-point" \ "Run from a subdirectory instead" fi # Data directory (optional) if [ -n "$mount2Dir" ] then if [ -d "$mount2Dir" ] then mount2Dir="$(cd "$mount2Dir" && pwd -P)" else echo "${0##*/}: ignore invalid -data directory: $mount2Dir" 1>&2 unset mount2Dir fi fi # Older (non-nss) user/group handling # userMapping() # { # echo '--volume=/etc/group:/etc/group:ro' # echo '--volume=/etc/passwd:/etc/passwd:ro' # echo '--volume=/etc/shadow:/etc/shadow:ro' # echo '--volume=/etc/sudoers.d:/etc/sudoers.d:ro' # } # Environment and settings for X11 forwarding # # See Also # https://stackoverflow.com/questions/48235040/run-x-application-in-a-docker-container-reliably-on-a-server-connected-via-ssh-w # https://blog.yadutaf.fr/2017/09/10/running-a-graphical-app-in-a-docker-container-on-a-remote-server/ # Prepare X11 mapping # - use tmp Xauthority file in local (mounted) directory unset display_host tmpXauth_host tmpXauth_guest if [ -n "$DISPLAY" ] \ && [ -n "$optX11Forwarding" ] \ && [ -d "$mount1Dir" ] \ && command -v xauth >/dev/null then # DISPLAY="host:0" vs DISPLAY=":0" display_host="${DISPLAY%%:*}" tmpXauth_host="$(mktemp --tmpdir="$mount1Dir" .Xauthority.container.XXXX)" trap "rm -f \"$tmpXauth_host\"; exit 0" EXIT TERM INT # Remove on exit # X-authority file to allow any hostname. Generally reasonably safe xauth nlist "$DISPLAY" | sed -e 's/^..../ffff/' | \ xauth -f "$tmpXauth_host" nmerge - # The mounted name tmpXauth_guest="${container_home}/${tmpXauth_host##*/}" fi # Define container run options for X11 mapping x11Mapping() { if [ -n "$tmpXauth_guest" ] then echo "--env=DISPLAY=$DISPLAY" echo "--env=XAUTHORITY=$tmpXauth_guest" # Accessing via ssh -X ('localhost:0' etc) or forced with -xhost if [ -n "$display_host" ] || [ "$optX11Forwarding" = host ] then echo "--net=host" elif [ -d /tmp/.X11-unix ] # No display host, bind sockets then echo "--volume=/tmp/.X11-unix:/tmp/.X11-unix" fi fi } if [ "$optVerbose" = true ] then set -x fi if [ -n "$optDryrun" ] then runPrefix="echo" echo "(dry-run)" 1>&2 echo 1>&2 else runPrefix="$sudo" fi if [ "$optVerbose" = true ] then set -x fi exec $runPrefix ${toolChain:?} run \ --rm -t -i \ --user="$guest_uid:$guest_gid" \ ${mount1Dir:+--volume="$mount1Dir:$container_home"} \ ${mount2Dir:+--volume="$mount2Dir:/data"} \ $(x11Mapping) \ ${optShmSize:+--shm-size="$optShmSize"} \ ${optEntrypoint:+--entrypoint="$optEntrypoint"} \ "$image" $optShellCommand "$@" # ---------------------------------------------------------------------------