#!/bin/sh
# (c) Copyright 2024. CodeWeavers, Inc.
name0=`basename "$0"`

# Portable which(1) implementation
cxwhich()
{
    case "$1" in
    /*)
        if [ -x "$1" -a -f "$1" ]
        then
            echo "$1"
            return 0
        fi
        ;;
    */*)
        if [ -x "`pwd`/$1" -a -f "`pwd`/$1" ]
        then
            echo "`pwd`/$1"
            return 0
        fi
        ;;
    *)
        saved_ifs="$IFS"
        IFS=":"
        for d in $PATH
        do
            IFS="$saved_ifs"
            if [ -n "$d" -a -x "$d/$1" -a -f "$d/$1" ]
            then
                echo "$d/$1"
                return 0
            fi
        done
        ;;
    esac
    return 1
}

# Locate where CrossOver is installed by looking for the directory
# where the cxmenu script is located, unwinding symlinks on the way
locate_cx_root()
{
    argv0=`cxwhich "$0"`
    [ -z "$argv0" ] && argv0="$0"
    echo "$argv0" | grep -E "^/" >/dev/null
    if [ $? -ne 0 ]
    then
        argv0="`pwd`/$argv0"
    fi
    dir=`dirname "$argv0"`
    bindir=`echo "$dir" | sed -e 's%/lib$%/bin%'`
    while [ ! -x "$bindir/cxmenu" -o ! -f "$bindir/cxmenu" ]
    do
        [ ! -h "$argv0" ] && break
        argv0=`ls -l "$argv0" | sed -e 's/^.*-> //'`
        echo "$argv0" | grep -E "^/" >/dev/null
        if [ $? -ne 0 ]
        then
            argv0="$dir/$argv0"
        fi
        dir=`dirname "$argv0"`
        bindir=`echo "$dir" | sed -e 's%/lib$%/bin%'`
    done
    bindir=`echo "$bindir" | sed -e 's%/\./\(\./\)*%/%g' -e 's%/\.$%%'`
    cx_root=`dirname "$bindir"`
    if [ ! -x "$cx_root/bin/cxmenu" -o ! -f "$cx_root/bin/cxmenu" ]
    then
        if [ "$1" = "--no-fail" ]
        then
            cx_root=""
            return 1
        fi
        echo "`basename \"$0\"`:error: could not find CrossOver in '$cx_root'" >&2
        exit 1
    fi
    return 0
}



error()
{
    echo "$name0:error: " "$@" >&2
}


#
# Process the command line
#

opt_init=""
opt_execmod=""
opt_textrel=""
usage=""

while [ $# -gt 0 ]
do
    arg="$1"
    case "$arg" in
    --init)          opt_init=1; shift ;;
    --is-execmod-on) opt_execmod=1; shift ;;
    --set-textrel)   opt_textrel="set"; shift ;;
    --auto-textrel)  opt_textrel="auto"; shift ;;
    --help|-h|-\?)   usage=0; shift ;;
    -*)
        error "unknown option '$arg'"
        usage=2
        break
        ;;
    *)  break ;;
    esac
done

if [ -z "$usage" ]
then
    if [ -n "$opt_execmod" -a \( -n "$opt_init" -o $# -gt 0 \) ]
    then
        error "--is-execmod-on is incompatible with the other options"
        usage=2
    fi
    if [ $# -eq 0 -a -z "$opt_execmod$opt_init" ]
    then
        error "you must specify the files or directories to apply textrel to"
        usage=2
    elif [ -z "$opt_textrel" ]
    then
        opt_textrel="auto"
    fi
fi

if [ -n "$usage" ]
then
    if [ "$usage" != "0" ]
    then
        error "try '$name0 --help' for more information"
        exit $usage
    fi
    cat <<EOF
Usage: $name0 [--init] [--is-execmod-on] [--set-textrel|--auto-textrel]
                 [--help] FILE|DIR...

Checks whether SELinux's execmod enforcement is turned on and/or set SELinux
contexts on the specified files and directories.

Where:
  --init          Set the SELinux contexts on CrossOver's files.
  --is-execmod-on Returns success if SELinux's execmod boolean is true.
  --set-textrel   Apply the textrel SELinux context to the specified files and
                  directories.
  --auto-textrel  Applies the textrel SELinux context if it is already set on
                  CrossOver's files.
  FILE            A file to set the specified SELinux context on.
  DIR             A directory to recursively set the specified SELinux context
                  on.
  --help, -h   Shows this help message.
EOF
    exit 0
fi

if [ -z "$CX_ROOT" ]
then
    locate_cx_root
    CX_ROOT="$cx_root"
fi


#
# --is-execmod-on
#

if [ -n "$opt_execmod" ]
then
    case `getsebool selinuxuser_execmod` in
    "selinuxuser_execmod --> off") exit 0 ;;
    "selinuxuser_execmod --> on") exit 1 ;;
    esac
    error "execmod enforcement status is unknown"
    exit 3
fi


#
# --init
#

setype=""
detect_setype()
{
    if chcon system_u:object_r:textrel_shlib_t:s0 "$1"
    then
        setype="textrel_shlib_t"
    else
        setype="lib_t"
    fi
}

if [ -n "$opt_init" ]
then
    # Note that this may or may not work depending on obscure system settings.
    # So try chcon on the landmark file and don't abort if that fails.
    if chcon system_u:object_r:bin_t:s0 "$CX_ROOT/bin/cxmenu"
    then
        find "$CX_ROOT/bin" -type d -prune -o -type f \
             -exec chcon system_u:object_r:bin_t:s0 {} \;
        find "$CX_ROOT/lib" -type d -prune -o -type f \
             -exec chcon system_u:object_r:lib_t:s0 {} \;
        if chcon system_u:object_r:wine_exec_t:s0 "$CX_ROOT"/bin/wine*-preloader
        then
            chcon system_u:object_r:wine_exec_t:s0 "$CX_ROOT"/bin/wineloader*
        fi
        detect_setype "$CX_ROOT/lib/wine"/*-windows/ntdll.dll
        find "$CX_ROOT/lib/wine" -type f \
             -exec chcon system_u:object_r:$setype:s0 {} \;
    fi
    [ $# -gt 0 ] || exit 0
fi


#
# --set-textrel and --auto-textrel
#

if [ -z "$setype" ]
then
    contexts=`ls -Z "$CX_ROOT/lib/wine"/*-windows/ntdll.dll`
    case "$contexts" in
    *:textrel_shlib_t:s0*) setype="textrel_shlib_t" ;;
    *:lib_t:s0*)           setype="lib_t" ;;
    esac
fi
if [ -z "$setype" ]
then
    if [ "$opt_textrel" = "auto" ]
    then
        echo "no context on CrossOver's files -> skipping"
        exit 0
    fi

    [ -n "$TMPDIR" ] || TMPDIR="/tmp"
    touch "$TMPDIR/$name0.$$"
    detect_setype "$TMPDIR/$name0.$$" 2>/dev/null
    rm -f "$TMPDIR/$name0.$$"
fi

for item in "$@"
do
    find "$item" -type f \
         -exec chcon system_u:object_r:$setype:s0 {} \;
done

exit 0
