#!/bin/bash
### BEGIN INIT INFO
# Provides:          realtime
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:
# Default-Stop:      0 1 6
# Short-Description: enable realtime capabilities when needed
# Description:       The rtai module is loaded when needed and
#  removed when the job has ended and is used throughout the LinuxCNC
#  infrastructure. The script has no effect for the PREEMPT realtime kernel.
#  The script is not meant to be started a boot time - it could be, though.
# X-Interactive:     false
### END INIT INFO
#
# ../scripts/realtime.  Generated from realtime.in by configure. 
# on Mon Aug 24 02:19:17 CEST 2026
#

export LANG=C

GREP=/usr/bin/grep
PS=/usr/bin/ps

CheckKernel() {
    # Shellcheck doesn't know about substitution
    # shellcheck disable=SC2194
    case "" in
    "") 
        ;;
    *)
        if [ "$(uname -r)" != "" ]; then
            cat 1>&2 << EOF
RTAPI requires the real-time kernel  to run.  Before running
this realtime application, reboot and choose this kernel at the boot menu.
EOF
            exit 1
        fi
        ;;
    esac
}

RUN_IN_PLACE=no
if [ $RUN_IN_PLACE = yes ]; then
    # When using RIP make sure PATH includes the directory where rtapi_app
    # resides
    PATH=/usr/bin:$PATH; export PATH
fi

CheckConfig(){
    # Shellcheck doesn't know about substitution
    # shellcheck disable=SC2034
    prefix=/usr
    # shellcheck disable=SC2034
    exec_prefix=${prefix}
    sysconfdir=/etc
    if [ "$RUN_IN_PLACE" = "yes" ]; then
        RTAPICONF=
        # check in the LinuxCNC scripts directory
        # $0 is the command to run this script
        # strip the script name to leave the path
        SCRIPT_DIR=${0%/*}
        # the path might be relative
        # convert it to an absolute path
        SCRIPT_DIR=$(realpath "$SCRIPT_DIR")
        # now look for rtapi.conf there
        if [ -f "$SCRIPT_DIR/rtapi.conf" ] ; then
            RTAPICONF=$SCRIPT_DIR/rtapi.conf
        fi
    else
        if [ -f "$sysconfdir/linuxcnc/rtapi.conf" ]; then
                RTAPICONF=$sysconfdir/linuxcnc/rtapi.conf
        fi
    fi
    if [ -z "$RTAPICONF" ] ; then
        echo "Missing rtapi.conf.  Check your installation." 1>&2
        exit 1
    fi
    INSMOD="/usr/bin/linuxcnc_module_helper insert"
    RMMOD="/usr/bin/linuxcnc_module_helper remove"
    LSMOD=""
    FUSER="/usr/bin/fuser"

    # Import the config
    source "$RTAPICONF"
    if [ ! -e "$RTLIB_DIR/.runinfo" ] && [ -e "$RTLIB_DIR/emc2/.runinfo" ]; then
        # installed system: we don't want our modules mixed up with rtai
        RTLIB_DIR=$RTLIB_DIR/emc2
    fi
    # Export the module path specified in the config.
    export MODPATH
    # Generate module lists for loading and unloading
    # lists contain RTOS modules plus RTAPI and HAL
    # unload list is in reverse order
    # any module names that are symlinks are resolved to their real names
    MODULES_LOAD=
    MODULES_UNLOAD=
    case $RTPREFIX in
        uspace)
            SHM_DEV=/dev/zero
            ;;
    esac
    for  MOD in $MODULES ; do
        eval MOD="\${MODPATH_${MOD}}"
        if [ -z "$MOD" ]; then continue; fi
        if [ -L "$MOD" ]; then
            MOD=${MOD%/*}/$(readlink "$MOD")
        fi
        MODULES_LOAD="$MODULES_LOAD $MOD"
        MOD="${MOD##*/}"
        MOD="${MOD%.*}"
        MODULES_UNLOAD="$MOD $MODULES_UNLOAD"
    done
    case $RTPREFIX in
        *rtai*)
            MODULES_LOAD="$MODULES_LOAD $RTLIB_DIR/rtapi$MODULE_EXT $RTLIB_DIR/hal_lib$MODULE_EXT"
            MODULES_UNLOAD="hal_lib rtapi $MODULES_UNLOAD"
            SHM_DEV=/dev/rtai_shm
            ;;
        *)
            MODULES_UNLOAD=
            ;;
    esac
}

CheckStatus(){
    case $RTPREFIX in
        uspace)
            if rtapi_app ping; then
                echo rtapi_app is running
                return 0
            elif [ -n "$($PS -o stat= -o comm= -C rtapi_app | $GREP -v '^Z')" ]; then
                echo rtapi_app is running but not reachable
                return 1
            else
                echo rtapi_app is not running
                return 1
            fi
            ;;
        *)
            # check loaded/unloaded status of modules
            unset NOTLOADED
            for MOD in $MODULES_UNLOAD ; do
                if "$LSMOD" | awk '{print $1}' | $GREP -x "$MOD" >/dev/null ; then
                    echo "$MOD is loaded"
                else
                    echo "$MOD is not loaded"
                    NOTLOADED=NOT
                fi
            done
            if [ -z "$NOTLOADED" ]; then
                return 0
            else
                return 1
            fi
            ;;
    esac
}

CheckMem(){
# check for user space processes using shared memory
    if [ -e /dev/mbuff ] ; then
        # device file exists, check for processes using it
        if $FUSER -s /dev/mbuff 2>/dev/null; then
            # at least one process is using it
            echo "ERROR:  Can't remove RTLinux modules, kill the following process(es) first"
            $FUSER -v /dev/mbuff
            exit 1
        fi
    elif [ -e /dev/rtai_shm ] ; then
        # device file exists, check for processes using it
        if $FUSER -s /dev/rtai_shm 2>/dev/null; then
            # at least one process is using it
            echo "ERROR:  Can't remove RTAI modules, kill the following process(es) first"
            $FUSER -v /dev/rtai_shm
            exit 1
        fi
    fi
}

Load(){
    CheckKernel
    case $RTPREFIX in
        uspace)
            rtapi_app start
            ;;
        *rtai*)
            for MOD in $MODULES_LOAD ; do
            if ! [ -d "/sys/module/$(basename "$MOD" .ko)" ]; then
                $INSMOD "$MOD" || return $?
            fi
            done
            if [ "$DEBUG" != "" ] && [ -w /proc/rtapi/debug ] ; then
                echo "$DEBUG" > /proc/rtapi/debug
            fi
            ;;
    esac
}

CheckLoaded(){
    # this abomination is needed because udev sometimes doesn't
    # have the device ready for us in time.
    n=0
    while [ $n -lt 100 ]; do
        [ -w "$SHM_DEV" ] && return 0
        echo "." 1>&2
        sleep .1
        n=$((n + 1))
    done
    echo "Can't write to $SHM_DEV - aborting" 1>&2
    exit 1
}

Unload(){
    CheckKernel

    case $RTPREFIX in
        uspace)
            rtapi_app exit

            # wait 5 seconds for rtapi_app to die and be reaped by its parent
            START=$SECONDS
            local NPROCS
            while [ 5 -gt $((SECONDS-START)) ]; do
                NPROCS=$(ps -C rtapi_app -o stat= -o comm= | $GREP -v '^Z' | wc -l)
                if [ "$NPROCS" -eq 0 ]; then
                    break
                fi
                sleep 0.1
            done
            NPROCS=$(ps -C rtapi_app -o stat= -o comm= | $GREP -v '^Z' | wc -l)
            if [ "$NPROCS" -gt 0 ]; then
                echo "ERROR: rtapi_app failed to die" 1>&2
            fi

            ipcrm -M 0x48414c32 2>/dev/null ;# HAL_KEY
            ipcrm -M 0x90280A48 2>/dev/null ;# RTAPI_KEY
            ipcrm -M 0x48484c34 2>/dev/null ;# UUID_KEY
            ;;
    esac

    for module in $MODULES_UNLOAD ; do
        $RMMOD "$module"
    done
}

Verify(){
    HAS_REALTIME=false
    case $RTPREFIX in
        uspace)
            rtapi_app check_rt && HAS_REALTIME=true
            ;;
        *rtai*)
            echo "RTAI"
            HAS_REALTIME=true
            ;;
    esac

    if [ $HAS_REALTIME = true ]; then
        return 0
    else
        return 1
    fi
}

CheckUnloaded(){
# checks to see if all modules were unloaded
    STATUS=
    for module in $MODULES_UNLOAD ; do
        # check to see if the module is installed
        if "$LSMOD" | awk '{print $1}' | $GREP -x "$module" >/dev/null ; then
            echo "ERROR: Could not unload '$module'"
            STATUS=error
        fi
    done
    if [ -n "$STATUS" ] ; then
        exit 1
    fi
}

CMD=$1

case "$CMD" in
    start|load)
        CheckConfig
        Load || exit $?
        CheckLoaded
        ;;
    restart|force-reload)
        CheckConfig
        CheckMem
        Unload
        CheckUnloaded
        Load || exit $?
        CheckLoaded
        ;;
    stop|unload)
        CheckConfig
        CheckMem
        Unload || exit $?
        ;;
    status)
        CheckConfig
        CheckStatus || exit $?
        ;;
    verify)
        CheckConfig
        Verify || exit $?
        ;;
    *)
        echo "Usage: $0 {start|load|stop|unload|restart|force-reload|status|verify}" >&2
        exit 1
        ;;
esac

exit 0

