#!/bin/sh ### BEGIN INIT INFO # Provides: clickhouse-server # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Required-Start: # Required-Stop: # Short-Description: Yandex clickhouse-server daemon ### END INIT INFO CLICKHOUSE_USER=clickhouse CLICKHOUSE_GROUP=${CLICKHOUSE_USER} SHELL=/bin/bash PROGRAM=clickhouse-server CLICKHOUSE_GENERIC_PROGRAM=clickhouse CLICKHOUSE_PROGRAM_ENV="" EXTRACT_FROM_CONFIG=${CLICKHOUSE_GENERIC_PROGRAM}-extract-from-config CLICKHOUSE_CONFDIR=/etc/$PROGRAM CLICKHOUSE_LOGDIR=/var/log/clickhouse-server CLICKHOUSE_LOGDIR_USER=root CLICKHOUSE_DATADIR_OLD=/opt/clickhouse CLICKHOUSE_DATADIR=/var/lib/clickhouse LOCALSTATEDIR=/var/lock CLICKHOUSE_BINDIR=/usr/bin CLICKHOUSE_CRONFILE=/etc/cron.d/clickhouse-server CLICKHOUSE_CONFIG=$CLICKHOUSE_CONFDIR/config.xml LOCKFILE=$LOCALSTATEDIR/$PROGRAM RETVAL=0 CLICKHOUSE_PIDDIR=/var/run/$PROGRAM CLICKHOUSE_PIDFILE="$CLICKHOUSE_PIDDIR/$PROGRAM.pid" # CLICKHOUSE_STOP_TIMEOUT=60 # Disabled by default. Place to /etc/default/clickhouse if you need. # Some systems lack "flock" command -v flock >/dev/null && FLOCK=flock # Override defaults from optional config file test -f /etc/default/clickhouse && . /etc/default/clickhouse # On x86_64, check for required instruction set. if uname -mpi | grep -q 'x86_64'; then if ! grep -q 'sse4_2' /proc/cpuinfo; then # On KVM, cpuinfo could falsely not report SSE 4.2 support, so skip the check. if ! grep -q 'Common KVM processor' /proc/cpuinfo; then # Some other VMs also report wrong flags in cpuinfo. # Tricky way to test for instruction set: # create temporary binary and run it; # if it get caught illegal instruction signal, # then required instruction set is not supported really. # # Generated this way: # gcc -xc -Os -static -nostdlib - <<< 'void _start() { __asm__("pcmpgtq %%xmm0, %%xmm1; mov $0x3c, %%rax; xor %%rdi, %%rdi; syscall":::"memory"); }' && strip -R .note.gnu.build-id -R .comment -R .eh_frame -s ./a.out && gzip -c -9 ./a.out | base64 -w0; echo if ! (echo -n 'H4sICAwAW1cCA2Eub3V0AKt39XFjYmRkgAEmBjsGEI+H0QHMd4CKGyCUAMUsGJiBJDNQNUiYlQEZOKDQclB9cnD9CmCSBYqJBRxQOvBpSQobGfqIAWn8FuYnPI4fsAGyPQz/87MeZtArziguKSpJTGLQK0mtKGGgGHADMSgoYH6AhTMPNHyE0NQzYuEzYzEXFr6CBPQDANAsXKTwAQAA' | base64 -d | gzip -d > /tmp/clickhouse_test_sse42 && chmod a+x /tmp/clickhouse_test_sse42 && /tmp/clickhouse_test_sse42); then echo 'Warning! SSE 4.2 instruction set is not supported' #exit 3 fi fi fi fi SUPPORTED_COMMANDS="{start|stop|status|restart|forcestop|forcerestart|reload|condstart|condstop|condrestart|condreload|initdb}" is_supported_command() { echo "$SUPPORTED_COMMANDS" | grep -E "(\{|\|)$1(\||})" &> /dev/null } is_running() { [ -r "$CLICKHOUSE_PIDFILE" ] && pgrep -s $(cat "$CLICKHOUSE_PIDFILE") 1> /dev/null 2> /dev/null } wait_for_done() { timeout=$1 attempts=0 while is_running; do attempts=$(($attempts + 1)) if [ -n "$timeout" ] && [ $attempts -gt $timeout ]; then return 1 fi sleep 1 done } die() { echo $1 >&2 exit 1 } # Check that configuration file is Ok. check_config() { if [ -x "$CLICKHOUSE_BINDIR/$EXTRACT_FROM_CONFIG" ]; then su -s $SHELL ${CLICKHOUSE_USER} -c "$CLICKHOUSE_BINDIR/$EXTRACT_FROM_CONFIG --config-file=\"$CLICKHOUSE_CONFIG\" --key=path" >/dev/null || die "Configuration file ${CLICKHOUSE_CONFIG} doesn't parse successfully. Won't restart server. You may use forcerestart if you are sure."; fi } initdb() { if [ -x "$CLICKHOUSE_BINDIR/$EXTRACT_FROM_CONFIG" ]; then CLICKHOUSE_DATADIR_FROM_CONFIG=$(su -s $SHELL ${CLICKHOUSE_USER} -c "$CLICKHOUSE_BINDIR/$EXTRACT_FROM_CONFIG --config-file=\"$CLICKHOUSE_CONFIG\" --key=path") if [ "(" "$?" -ne "0" ")" -o "(" -z "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ")" ]; then die "Cannot obtain value of path from config file: ${CLICKHOUSE_CONFIG}"; fi echo "Path to data directory in ${CLICKHOUSE_CONFIG}: ${CLICKHOUSE_DATADIR_FROM_CONFIG}" else CLICKHOUSE_DATADIR_FROM_CONFIG=$CLICKHOUSE_DATADIR fi if ! getent group ${CLICKHOUSE_USER} >/dev/null; then echo "Can't chown to non-existing user ${CLICKHOUSE_USER}" return fi if ! getent passwd ${CLICKHOUSE_GROUP} >/dev/null; then echo "Can't chown to non-existing group ${CLICKHOUSE_GROUP}" return fi if ! $(su -s $SHELL ${CLICKHOUSE_USER} -c "test -r ${CLICKHOUSE_CONFIG}"); then echo "Warning! clickhouse config [${CLICKHOUSE_CONFIG}] not readable by user [${CLICKHOUSE_USER}]" fi if ! $(su -s $SHELL ${CLICKHOUSE_USER} -c "test -O \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\" && test -G \"${CLICKHOUSE_DATADIR_FROM_CONFIG}\""); then if [ $(dirname "${CLICKHOUSE_DATADIR_FROM_CONFIG}") = "/" ]; then echo "Directory ${CLICKHOUSE_DATADIR_FROM_CONFIG} seems too dangerous to chown." else if [ ! -e "${CLICKHOUSE_DATADIR_FROM_CONFIG}" ]; then echo "Creating directory ${CLICKHOUSE_DATADIR_FROM_CONFIG}" mkdir -p "${CLICKHOUSE_DATADIR_FROM_CONFIG}" fi echo "Changing owner of [${CLICKHOUSE_DATADIR_FROM_CONFIG}] to [${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP}]" chown -R ${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP} "${CLICKHOUSE_DATADIR_FROM_CONFIG}" fi fi if ! $(su -s $SHELL ${CLICKHOUSE_USER} -c "test -w ${CLICKHOUSE_LOGDIR}"); then echo "Changing owner of [${CLICKHOUSE_LOGDIR}/*] to [${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP}]" chown -R ${CLICKHOUSE_USER}:${CLICKHOUSE_GROUP} ${CLICKHOUSE_LOGDIR}/* echo "Changing owner of [${CLICKHOUSE_LOGDIR}] to [${CLICKHOUSE_LOGDIR_USER}:${CLICKHOUSE_GROUP}]" chown ${CLICKHOUSE_LOGDIR_USER}:${CLICKHOUSE_GROUP} ${CLICKHOUSE_LOGDIR} fi } start() { [ -x $CLICKHOUSE_BINDIR/$PROGRAM ] || exit 0 local EXIT_STATUS EXIT_STATUS=0 echo -n "Start $PROGRAM service: " if is_running; then echo -n "already running " EXIT_STATUS=1 else ulimit -n 262144 mkdir -p $CLICKHOUSE_PIDDIR chown -R $CLICKHOUSE_USER:$CLICKHOUSE_GROUP $CLICKHOUSE_PIDDIR initdb if ! is_running; then # Lock should not be held while running child process, so we release the lock. Note: obviously, there is race condition. # But clickhouse-server has protection from simultaneous runs with same data directory. su -s $SHELL ${CLICKHOUSE_USER} -c "$FLOCK -u 9; $CLICKHOUSE_PROGRAM_ENV exec -a \"$PROGRAM\" \"$CLICKHOUSE_BINDIR/$PROGRAM\" --daemon --pid-file=\"$CLICKHOUSE_PIDFILE\" --config-file=\"$CLICKHOUSE_CONFIG\"" EXIT_STATUS=$? if [ $EXIT_STATUS -ne 0 ]; then break fi fi fi if [ $EXIT_STATUS -eq 0 ]; then attempts=0 while ! is_running && [ $attempts -le ${CLICKHOUSE_START_TIMEOUT:=10} ]; do attempts=$(($attempts + 1)) sleep 1 done if is_running; then echo "DONE" else echo "UNKNOWN" fi else echo "FAILED" fi return $EXIT_STATUS } stop() { #local EXIT_STATUS EXIT_STATUS=0 if [ -f $CLICKHOUSE_PIDFILE ]; then echo -n "Stop $PROGRAM service: " kill -TERM $(cat "$CLICKHOUSE_PIDFILE") if ! wait_for_done ${CLICKHOUSE_STOP_TIMEOUT}; then EXIT_STATUS=2 echo "TIMEOUT" else echo "DONE" fi fi return $EXIT_STATUS } restart() { check_config if stop; then if start; then return 0 fi fi return 1 } forcestop() { local EXIT_STATUS EXIT_STATUS=0 echo -n "Stop forcefully $PROGRAM service: " kill -KILL $(cat "$CLICKHOUSE_PIDFILE") wait_for_done echo "DONE" return $EXIT_STATUS } service_or_func() { if [ -x "/bin/systemctl" ] && [ -f /etc/systemd/system/clickhouse-server.service ] && [ -d /run/systemd/system ]; then service $PROGRAM $1 else $1 fi } forcerestart() { forcestop # Should not use 'start' function if systemd active service_or_func start } use_cron() { # 1. running systemd if [ -x "/bin/systemctl" ] && [ -f /etc/systemd/system/clickhouse-server.service ] && [ -d /run/systemd/system ]; then return 1 fi # 2. disabled by config if [ -z "$CLICKHOUSE_CRONFILE" ]; then return 2 fi return 0 } enable_cron() { use_cron && sed -i 's/^#*//' "$CLICKHOUSE_CRONFILE" } disable_cron() { use_cron && sed -i 's/^#*/#/' "$CLICKHOUSE_CRONFILE" } is_cron_disabled() { use_cron || return 0 # Assumes that either no lines are commented or all lines are commented. # Also please note, that currently cron file for ClickHouse has only one line (but some time ago there was more). grep -q -E '^#' "$CLICKHOUSE_CRONFILE"; } main() { # See how we were called. EXIT_STATUS=0 case "$1" in start) start && enable_cron ;; stop) # disable_cron returns false if cron disabled (with systemd) - not checking return status disable_cron stop ;; restart) restart && enable_cron ;; forcestop) disable_cron forcestop ;; forcerestart) forcerestart && enable_cron ;; reload) restart ;; condstart) is_running || service_or_func start ;; condstop) is_running && service_or_func stop ;; condrestart) is_running && service_or_func restart ;; condreload) is_running && service_or_func restart ;; initdb) initdb ;; enable_cron) enable_cron ;; disable_cron) disable_cron ;; *) echo "Usage: $0 $SUPPORTED_COMMANDS" exit 2 ;; esac exit $EXIT_STATUS } status() { if is_running; then echo "$PROGRAM service is running" exit 0 else if is_cron_disabled; then echo "$PROGRAM service is stopped"; else echo "$PROGRAM: process unexpectedly terminated" fi exit 3 fi } # Running commands without need of locking case "$1" in status) status ;; esac ( if $FLOCK -n 9; then main "$@" else echo "Init script is already running" && exit 1 fi ) 9> $LOCKFILE