#!/bin/sh -e
# ============================================================
#  pulp_celery_workers - Start the Pulp Celery worker daemons.
# ============================================================
#
# :Usage: /etc/init.d/pulp_celery_workers {start|stop|force-reload|restart|try-restart|status}
# :Configuration file: /etc/default/pulp_celery_workers
#
# See http://docs.celeryproject.org/en/latest/tutorials/daemonizing.html#generic-init-scripts


### BEGIN INIT INFO
# Provides:          pulp_celery_workers
# Required-Start:    $network $local_fs $remote_fs
# Required-Stop:     $network $local_fs $remote_fs
# Should-Start:      mongod qpidd rabbitmq-server
# Default-Start:     3 4 5
# Default-Stop:      0 1 2 6
# Short-Description: pulp's celery task worker daemon
### END INIT INFO

VERSION=10.0
echo "celery init v${VERSION}."
if [ "$EUID" != "0" ]; then
    echo "Error: This program can only be used by the root user."
    echo "       Unprivileged users must use the 'celery multi' utility, "
    echo "       or 'celery worker --detach'."
    exit 1
fi


# Can be a runlevel symlink (e.g. S02celeryd)
if [ -L "$0" ]; then
    SCRIPT_FILE=$(readlink "$0")
else
    SCRIPT_FILE="$0"
fi
SCRIPT_NAME="$(basename "$SCRIPT_FILE")"

DEFAULT_USER="celery"
DEFAULT_PID_FILE="/var/run/pulp/%n.pid"
DEFAULT_LOG_FILE="/var/log/celery/%n.log"
DEFAULT_LOG_LEVEL="INFO"
DEFAULT_NODES="celery"
DEFAULT_CELERYD="-m celery worker --detach"
DEFAULT_PULP_CONCURRENCY=$(nproc --all)

CELERY_DEFAULTS=${CELERY_DEFAULTS:-"/etc/default/${SCRIPT_NAME}"}


if [ -f "$CELERY_DEFAULTS" ]; then
    echo "Using config script: $CELERY_DEFAULTS"
    . "$CELERY_DEFAULTS"
fi

# Sets --app argument for CELERY_BIN
CELERY_APP_ARG=""
if [ ! -z "$CELERY_APP" ]; then
    CELERY_APP_ARG="--app=$CELERY_APP"
fi

# Sets --maxtasksperchild argument for CELERY_BIN
if [ -z ${PULP_MAX_TASKS_PER_CHILD+x} ]; then
    CELERY_MAX_TASKS_PER_CHILD_ARG=""
else
    CELERY_MAX_TASKS_PER_CHILD_ARG="--maxtasksperchild=$PULP_MAX_TASKS_PER_CHILD"
fi

CELERYD_USER=${CELERYD_USER:-$DEFAULT_USER}

# Set CELERY_CREATE_DIRS to always create log/pid dirs.
CELERY_CREATE_DIRS=${CELERY_CREATE_DIRS:-0}
CELERY_CREATE_RUNDIR=$CELERY_CREATE_DIRS
CELERY_CREATE_LOGDIR=$CELERY_CREATE_DIRS
if [ -z "$CELERYD_PID_FILE" ]; then
    CELERYD_PID_FILE="$DEFAULT_PID_FILE"
    CELERY_CREATE_RUNDIR=1
fi
if [ -z "$CELERYD_LOG_FILE" ]; then
    CELERYD_LOG_FILE="$DEFAULT_LOG_FILE"
    CELERY_CREATE_LOGDIR=1
fi

CELERYD_LOG_LEVEL=${CELERYD_LOG_LEVEL:-${CELERYD_LOGLEVEL:-$DEFAULT_LOG_LEVEL}}
CELERY_BIN=${CELERY_BIN:-"celery"}
CELERYD_MULTI=${CELERYD_MULTI:-"$CELERY_BIN multi"}

export CELERY_LOADER

if [ -n "$2" ]; then
    CELERYD_OPTS="$CELERYD_OPTS $2"
fi

CELERYD_LOG_DIR=`dirname $CELERYD_LOG_FILE`
CELERYD_PID_DIR=`dirname $CELERYD_PID_FILE`

# Extra start-stop-daemon options, like user/group.
if [ -n "$CELERYD_CHDIR" ]; then
    DAEMON_OPTS="$DAEMON_OPTS --workdir=$CELERYD_CHDIR"
fi


CELERYD_NODES=${CELERYD_NODES:-$DEFAULT_NODES}
PULP_CONCURRENCY=${PULP_CONCURRENCY:-$DEFAULT_PULP_CONCURRENCY}
# If the user has specified a concurrency setting, we will add that many reserved_resource_workers to the
#  CELERYD_NODES string here
if [ $PULP_CONCURRENCY -ge 1 ]; then
    for i in $( seq 0 $( expr $PULP_CONCURRENCY - 1)); do
        CELERYD_NODES_ARRAY[i]="reserved_resource_worker-$i"
    done;
    CELERYD_NODES="$CELERYD_NODES ${CELERYD_NODES_ARRAY[@]}"
fi


check_dev_null() {
    if [ ! -c /dev/null ]; then
        echo "/dev/null is not a character device!"
        exit 75  # EX_TEMPFAIL
    fi
}


maybe_die() {
    if [ $? -ne 0 ]; then
        echo "Exiting: $* (errno $?)"
        exit 77  # EX_NOPERM
    fi
}

create_default_dir() {
    if [ ! -d "$1" ]; then
        echo "- Creating default directory: '$1'"
        mkdir -p "$1"
        maybe_die "Couldn't create directory $1"
        echo "- Changing permissions of '$1' to 02750"
        chmod 02750 "$1"
        maybe_die "Couldn't change permissions for $1"
        if [ -n "$CELERYD_USER" ]; then
            echo "- Changing owner of '$1' to '$CELERYD_USER'"
            chown "$CELERYD_USER" "$1"
            maybe_die "Couldn't change owner of $1"
        fi
        if [ -n "$CELERYD_GROUP" ]; then
            echo "- Changing group of '$1' to '$CELERYD_GROUP'"
            chgrp "$CELERYD_GROUP" "$1"
            maybe_die "Couldn't change group of $1"
        fi
        echo "- Restoring context of $1 to 'pulp_var_run_t'"
        restorecon /var/run/pulp
        maybe_die "Couldn't restore context of $1"
    fi
}


check_paths() {
    if [ $CELERY_CREATE_LOGDIR -eq 1 ]; then
        create_default_dir "$CELERYD_LOG_DIR"
    fi
    if [ $CELERY_CREATE_RUNDIR -eq 1 ]; then
        create_default_dir "$CELERYD_PID_DIR"
    fi
}

create_paths() {
    create_default_dir "$CELERYD_LOG_DIR"
    create_default_dir "$CELERYD_PID_DIR"
}

export PATH="${PATH:+$PATH:}/usr/sbin:/sbin"


_get_pids() {
    found_pids=0
    my_exitcode=0

    for pid_file in "$CELERYD_PID_DIR"/*.pid; do
        local pid=`cat "$pid_file"`
        local cleaned_pid=`echo "$pid" | sed -e 's/[^0-9]//g'`
        if [ -z "$pid" ] || [ "$cleaned_pid" != "$pid" ]; then
            echo "bad pid file ($pid_file)"
            one_failed=true
            my_exitcode=1
        else
            found_pids=1
            echo "$pid"
        fi

    if [ $found_pids -eq 0 ]; then
        echo "${SCRIPT_NAME}: All nodes down"
        exit $my_exitcode
    fi
    done
}


_chuid () {
    su - "$CELERYD_USER" -s /bin/sh -c "$CELERYD_MULTI $*"
}

# this function implements the fix for bz #1145723
write_log_message () {
  if [ $PULP_CONCURRENCY -ge 1 ]; then
    for i in "${CELERYD_NODES_ARRAY[@]}"
    do
      local log_file="`dirname $CELERYD_LOG_FILE`/${i}.log"
      touch $log_file && chown $CELERYD_USER $log_file
      chmod 640 $log_file
      echo -n `date "+%Y-%m-%d %T"` >> $log_file
      echo " $1" >> $log_file
    done
  else
    local log_file="`dirname $CELERYD_LOG_FILE`/${CELERYD_NODES}.log"
    touch $log_file && chown $CELERYD_USER $log_file
    chmod 640 $log_file
    echo -n `date "+%Y-%m-%d %T"` >> $log_file
    echo " $1" >> $log_file
  fi
}

start_workers () {
    if [ ! -z "$CELERYD_ULIMIT" ]; then
        ulimit $CELERYD_ULIMIT
    fi
    _chuid $* start $CELERYD_NODES $DAEMON_OPTS     \
                 --pidfile="$CELERYD_PID_FILE"      \
                 --logfile="$CELERYD_LOG_FILE"      \
                 --loglevel="$CELERYD_LOG_LEVEL"    \
                 $CELERY_APP_ARG                    \
                 $CELERY_MAX_TASKS_PER_CHILD_ARG    \
                 $CELERYD_OPTS
}


dryrun () {
    (C_FAKEFORK=1 start_workers --verbose)
}


stop_workers () {
    _chuid stopwait $CELERYD_NODES -QUIT --pidfile="$CELERYD_PID_FILE"
}


kill_workers() {
    _chuid kill $CELERYD_NODES --pidfile="$CELERYD_PID_FILE"
}


restart_workers_graceful () {
    local worker_pids=
    worker_pids=`_get_pids`
    [ "$one_failed" ] && exit 1

    for worker_pid in $worker_pids; do
        local failed=
        kill -HUP $worker_pid 2> /dev/null || failed=true
        if [ "$failed" ]; then
            echo "${SCRIPT_NAME} worker (pid $worker_pid) could not be restarted"
            one_failed=true
        else
            echo "${SCRIPT_NAME} worker (pid $worker_pid) received SIGHUP"
        fi
    done

    [ "$one_failed" ] && exit 1 || exit 0
}


check_status () {
    my_exitcode=0
    found_pids=0

    local one_failed=
    for node in $CELERYD_NODES; do
        local pid_file="$CELERYD_PID_DIR/$node.pid"
        if [ ! -e $pid_file ]; then
            local pid=
        else
            local pid=`cat "$pid_file"`
        fi

        if [ -z "$pid" ]; then
            echo "node $node is stopped..."
            one_failed=true
        else
            local failed=
            kill -0 $pid 2> /dev/null || failed=true
            if [ "$failed" ]; then
                echo "node $node (pid $pid) is missing..."
                one_failed=true
            else
                echo "node $node (pid $pid) is running..."
            fi
        fi
    done

    [ "$one_failed" ] && exit 1 || exit 0
}


case "$1" in
    start)
        check_dev_null
        check_paths
        write_log_message "********************************************************"
        write_log_message "* Celery startup requested. After startup is complete, *"
        write_log_message "* messages will be logged to /var/log/messages.        *"
        write_log_message "********************************************************"
        start_workers
    ;;

    stop)
        check_dev_null
        check_paths
        stop_workers
    ;;

    reload|force-reload)
        echo "Use restart"
    ;;

    status)
        check_status
    ;;

    restart)
        check_dev_null
        check_paths
        stop_workers
        write_log_message "********************************************************"
        write_log_message "* Celery startup requested. After startup is complete, *"
        write_log_message "* messages will be logged to /var/log/messages.        *"
        write_log_message "********************************************************"
        start_workers
    ;;

    graceful)
        check_dev_null
        restart_workers_graceful
    ;;

    kill)
        check_dev_null
        kill_workers
    ;;

    dryrun)
        check_dev_null
        dryrun
    ;;

    try-restart)
        check_dev_null
        check_paths
        restart_workers
    ;;

    create-paths)
        check_dev_null
        create_paths
    ;;

    check-paths)
        check_dev_null
        check_paths
    ;;

    *)
        echo "Usage: /etc/init.d/${SCRIPT_NAME} {start|stop|restart|graceful|kill|dryrun|create-paths}"
        exit 64  # EX_USAGE
    ;;
esac

exit 0
