blob: 504a64d72899439547c194a1c793f30e45958719 [file] [log] [blame]
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
###
### Shell script functions for Solr
###
#
##### Setup
DEFAULT_AGENT_HEAP=16M
DEFAULT_AGENT_MAIN_ETC_DIR=/etc/solr
DEFAULT_AGENT_PORT=58983
DEFAULT_CLOUD_BUILD_ADDRESS=239.89.83.0
DEFAULT_CLOUD_BUILD_PORT=8983
DEFAULT_SOLR_HEAP=512M
DEFAULT_SOLR_PORT=8983
DEFAULT_X_WHICH=/usr/bin/which
DEFAULT_ZK_CLIENT_PORT=2181
DEFAULT_ZK_ELECTION_PORT=3888
DEFAULT_ZK_LEADER_PORT=2888
##### End Setup
#
#----
# DO NOT EDIT BELOW THIS LINE WITHOUT GUIDANCE
#----
if [ -z "${AGENT_HEAP}" ]; then
AGENT_HEAP="${DEFAULT_AGENT_HEAP}"
fi
if [ -z "${AGENT_MAIN_ETC_DIR}" ]; then
AGENT_MAIN_ETC_DIR="${DEFAULT_AGENT_MAIN_ETC_DIR}"
fi
if [ -z "${AGENT_PORT}" ]; then
AGENT_PORT="${DEFAULT_AGENT_PORT}"
fi
if [ -z "${CLOUD_BUILD_ADDRESS}" ]; then
CLOUD_BUILD_ADDRESS="${DEFAULT_CLOUD_BUILD_ADDRESS}"
fi
if [ -z "${CLOUD_BUILD_PORT}" ]; then
CLOUD_BUILD_PORT="${DEFAULT_CLOUD_BUILD_PORT}"
fi
if [ -z "${SOLR_HEAP}" ]; then
SOLR_HEAP="${DEFAULT_SOLR_HEAP}"
fi
if [ -z "${SOLR_PORT}" ]; then
SOLR_PORT="${DEFAULT_SOLR_PORT}"
fi
if [ -z "${X_WHICH}" ]; then
X_WHICH="${DEFAULT_X_WHICH}"
fi
if [ -z "${ZK_CLIENT_PORT}" ]; then
ZK_CLIENT_PORT="${DEFAULT_ZK_CLIENT_PORT}"
fi
if [ -z "${ZK_ELECTION_PORT}" ]; then
ZK_ELECTION_PORT="${DEFAULT_ZK_ELECTION_PORT}"
fi
if [ -z "${ZK_LEADER_PORT}" ]; then
ZK_LEADER_PORT="${DEFAULT_ZK_LEADER_PORT}"
fi
# Write a message to stderr indicating that a command is required.
# If no command is provided to this function, use a generic message.
# Exit the script after writing the message.
exit_err_msg_required_command() {
REQ_CMD="$1"
if [ -z "${REQ_CMD}" ]; then
echo >&2 "A command required to run this script is missing."
else
echo >&2 "The ${REQ_CMD} command is required to run this script."
fi
err_msg_contact_community
exit 1
}
# Write a message asking the user to contact the Solr community if they
# feel that there's a problem.
err_msg_contact_community() {
echo >&2 "---"
echo >&2 "If you feel this is in error, please mention the problem"
echo >&2 "to the Solr community on the mailing list or IRC channel."
echo >&2 "http://lucene.apache.org/solr/community.html#mailing-lists-irc"
}
# Locate directories based based on a script directory passed in as the first
# argument and set environment variables. The current working directory will
# be set to the parent of that directory and assigned to SOLR_TIP.
find_dirs_and_change_cwd() {
MYDIR="$1"
cd ${MYDIR}
cd ..
SOLR_TIP=`pwd`
LOCAL_ETC_DIR=${SOLR_TIP}/etc
SOLR_LIB_DIR=${SOLR_TIP}/lib
SOLR_TMP_DIR=${SOLR_TIP}/tmp
SOLR_RUN_DIR=${SOLR_TIP}/run
}
# Find required system executables or shell builtin commands and set
# variables like X_TEMPFILE so the script has a better chance of running
# on systems where things are installed outside of LSB locations.
find_system_programs_or_exit() {
# Make sure the which command is available.
if [ ! -x "${X_WHICH}" ]; then
echo >&2 "The command ${X_WHICH} is not found or not executable."
exit_err_msg_required_command "${X_WHICH}"
fi
# Check for the existence of other commands we need.
locate_command_or_exit tempfile X_TEMPFILE 1
locate_command_or_exit find X_FIND 1
locate_command_or_exit xargs X_XARGS 1
# We look for lsof and netstat, but do not exit
# if they are not found.
locate_command_or_exit lsof X_LSOF 0
locate_command_or_exit netstat X_NETSTAT 0
# If NEITHER lsof or netstat is found, log a warning message.
if [-z "${X_LSOF}" ] && [ -z "${X_NETSTAT}" ]; then
echo >&2 "Neither lsof or netstat found. Will not be able to check for"
echo >&2 "programs already listening on relevant ports."
fi
}
# Check whether a program exists as a builtin or an executable. Will not
# validate a command that is a function or an alias. Assign a dynamic
# variable to the bare program name or an executable file path as required
# if found, exit the script if not. The first argument is the bare command,
# the second argument is the variable that will be assigned with the correct
# command or executable. The third argument is an exit flag -- a numeric
# boolean value (usually 0 or 1) indicating whether or not a missing program
# will cause a script exit. A missing program will result in an error message
# whether an exit is forced or not. The first two arguments are required.
# The exit flag will default to 1 if not present. If the variable mentioned
# as the second argument is already defined, then the value of that variable
# will override the first argument to this script and tested to make sure it
# is a valid command. This allows somebody to define variables like X_TEMPFILE
# in advance to override the automatic detection, while making sure that the
# explicitly set value is actually a good command.
locate_command_or_exit() {
PRGNAME=$1
shift
PRGVAR=$1
shift
EXITFLAG=$1
if [ -z "${PRGNAME}" ] || [ -z "${PRGVAR}" ]; then
echo >&2 "locate_command_or_exit routine missing required arguments."
echo >&2 "Aborting script."
err_msg_contact_community
exit 1
fi
if [ -z "${EXITFLAG}" ]; then
EXITFLAG=1;
fi
BADPROG=0
if [ -n "${!PRGVAR}" ]; then
echo >&2 "The ${PRGNAME} command has been predefined as ${!PRGVAR}."
PRGNAME="${!PRGVAR}"
fi
CMD_TYPE=`type -t ${PRGNAME}`
if [ "${CMD_TYPE}" == "builtin" ]; then
eval ${PRGVAR}="\${PRGNAME}"
elif [ "${CMD_TYPE}" == "file" ]; then
XPATH=`"${X_WHICH}" "${PRGNAME}"`
RET=$?
if [ $RET -eq 0 ]; then
if [ -x "${XPATH}" ]; then
eval ${PRGVAR}="\${XPATH}"
else
BADPROG=1
fi
else
BADPROG=1
fi
else
BADPROG=1
fi
if [ ${BADPROG} -ne 0 ]; then
echo >&2 "The ${PRGNAME} command is not found or not executable."
echo >&2 "The ${PRGVAR} env variable can explicitly set its location."
if [ ${EXITFLAG} -ne 0 ]; then
exit_err_msg_required_command "${PRGNAME}"
fi
fi
return 0
}
# Find Java and set the JAVA variable.
# Exit script if unable to locate a java executable.
find_java_or_exit() {
JAVA=
if [ -n "$SOLR_JAVA_HOME" ]; then
echo >&2 "SOLR_JAVA_HOME found, using it as JAVA_HOME."
JAVA_HOME="$SOLR_JAVA_HOME"
fi
if [ -n "$JAVA_HOME" ]; then
for java in "${JAVA_HOME}/bin/amd64/java" "${JAVA_HOME}/bin/java"; do
if [ -x "$java" ]; then
JAVA="$java"
break
fi
done
if [ -z "$JAVA" ]; then
echo >&2 "The currently defined JAVA_HOME (${JAVA_HOME}) refers"
echo >&2 "to a location where Java could not be found. Aborting."
echo >&2 "Either fix the variable or remove it from the environment"
echo >&2 "so that the system PATH will be searched."
err_msg_contact_community
exit 1
fi
else
X_JAVA=`"${X_WHICH}" java`
if [ -x "${X_JAVA}" ]; then
JAVA=${X_JAVA}
else
echo >&2 "Unable to locate a java executable."
err_msg_contact_community
exit 1
fi
fi
return 0
}
# Start the agent. Will pass all parameters to the startup.
# The first parameter is the properties file to use.
agent_start() {
start_agent_program $@ start
}
# Start the agent program with a commandline parameter that tells it
# to examine its config and the system, and use what it finds to create
# a bunch of environment variables. The agent will output the commands
# for the variables to stdout.
agent_get_env() {
start_agent_program get_env
return $?
}
# Start the agent with arguments sent to this function.
# Removes all options (things like -D), leaving only commands for the agent.
# Will set a SOLR_AGENT_PROPERTIES sysprop with that variable value.
start_agent_program() {
CP_JARS=`get_jars_for_classpath ${SOLR_TIP}/lib/log ${SOLR_TIP}/lib/solrj`
OPTIND=1
while getopts ":D:" opt; do
case ${opt} in
D)
# Do nothing. This will remove the option. May not need this case.
;;
*)
# Do nothing. This will remove the option.
;;
esac
done
shift $((OPTIND -1))
$JAVA -Xmx${AGENT_HEAP} -cp ${CP_JARS} -DSOLR_AGENT_PROPERTIES="${SOLR_AGENT_PROPERTIES}" -jar lib/agent.jar $@
return $?
}
# Given a list of path locations, recursively find all the jars and write a
# classpath string to stdout.
get_jars_for_classpath() {
get_files_for_classpath jar $@
}
# Given an extension and a list of of path locations, recursively find all
# the files with that extension and write a classpath string to stdout. To
# include all files, pass an equal sign in as the extension.
get_files_for_classpath() {
CP_EXTENSION=$1
shift
if [ "${CP_EXTENSION}" == "=" ]; then
CP_FILESPEC="*"
else
CP_FILESPEC="*.${CP_EXTENSION}"
fi
DETECTED_CLASSPATH=
ADDED_FIRST_CP_ENTRY=
KEEP_RUNNING=true
while [ -n "${KEEP_RUNNING}" ]; do
CHECKDIR=$1
shift
if [ -n "${CHECKDIR}" ]; then
if [ -d "${CHECKDIR}" ]; then
while IFS= read -r -d '' line; do
add_to_detected_classpath "$line"
done < <(${X_FIND} ${CHECKDIR} -type f -name "${CP_FILESPEC}" -print0)
fi
else
KEEP_RUNNING=
fi
done
echo "$DETECTED_CLASSPATH"
}
add_to_detected_classpath() {
CP_ENTRY="$1"
if [ -n "${ADDED_FIRST_CP_ENTRY}" ]; then
# If the first entry in the classpath has been added
# we add a colon before the entry as a separator.
CP_ENTRY=":${CP_ENTRY}"
fi
# Set the value to indicate the first entry has been added.
ADDED_FIRST_CP_ENTRY=true
DETECTED_CLASSPATH="${DETECTED_CLASSPATH}${CP_ENTRY}"
}
# Locate the agent properties file based on available information.
# The SOLR_AGENT_PROPERTIES variable may be empty after this runs,
# but only if this is a non-service install.
find_agent_properties_or_exit() {
# First, if service name is not set, try to figure out a service name.
# When no service name set and only one service is installed, we assume
# that service. If multiple services installed and no name is set, the
# script will abort.
if [ -z "${SOLR_SERVICE_NAME}" ]; then
SOLR_SERVICES_CONFIG=${LOCAL_ETC_DIR}/services.conf
declare -a SERVICES
if [ -r "${SOLR_SERVICES_CONFIG}" ]; then
readarray -t SERVICES < ${SOLR_SERVICES_CONFIG}
fi
if [ ${#SERVICES[@]} -gt 0 ]; then
if [ ${#SERVICES[@]} -eq 1 ]; then
SOLR_SERVICE_NAME=${SERVICES[0]}
else
echo >&2 "Multiple services detected in ${SOLR_SERVICES_CONFIG}"
echo >&2 "SOLR_SERVICE_NAME must be defined to proceed."
err_msg_contact_community
exit 1
fi
fi
fi
# If we reach here, then we have either settled on one service name or
# there are no services.
if [ -n "${SOLR_SERVICE_NAME}" ]; then
echo >&2 "Solr service name ${SOLR_SERVICE_NAME} detected."
SOLR_AGENT_PROPERTIES=${MAIN_ETC_DIR}/${SOLR_SERVICE_NAME}/solr-agent.properties
else
SOLR_AGENT_PROPERTIES=${LOCAL_ETC_DIR}/solr-agent.properties
fi
# When running as a service, the agent properties file must exist. When the
# properties file does not exist, decide whether to exit or use defaults.
if [ ! -r "${SOLR_AGENT_PROPERTIES}" ]; then
if [ -n "${SOLR_SERVICE_NAME}" ]; then
echo >&2 "The ${SOLR_AGENT_PROPERTIES} file is not found or not readable."
echo >&2 "Service installs must have an agent properties file."
err_msg_contact_community
exit 1
else
echo >&2 "The ${SOLR_AGENT_PROPERTIES} file is not found or not readable."
echo >&2 "Proceeding with defaults for a non-service install."
SOLR_AGENT_PROPERTIES=
fi
# TODO: Service recommendation might need to move.
if [ -z "${SOLR_SERVICE_NAME}" ]; then
recommend_service
fi
fi
}
recommend_service() {
echo >&2 "Non-service install detected."
echo >&2 "Installing Solr as a service is strongly recommended."
echo >&2 "https://lucene.apache.org/solr/guide/taking-solr-to-production.html"
}
# Determine whether or not the configured version of Java supports the
# ExitOnOutOfMemoryError flag. Sets JAVA_OOM_EXIT_SUPPORTED with a numeric
# boolean value and returns the exit code from the attempt to run Java with
# that option.
check_oom_exit_supported() {
$JAVA -Xmx1M -XX:+ExitOnOutOfMemoryError -version > /dev/null 2> /dev/null
RET=$?
if [ ${RET} -eq 0 ]; then
JAVA_OOM_EXIT_SUPPORTED=1
else
JAVA_OOM_EXIT_SUPPORTED=0
fi
return ${RET}
}
# If the BASH_VERSION variable is not set, it is a reasonable indication
# that the shell we are running in is not bash. If that situation is
# detected, exit.
exit_if_no_bash() {
if [ -z "${BASH_VERSION}" ]; then
echo >&2 "This script must be run by the bash shell."
err_msg_contact_community
exit 1
fi
}
# Exit script if the bash version is too old. Require version 4.
# The first release of bash 4 was in 2009.
exit_if_bash_too_old() {
if [ -n "${BASH_VERSINFO[*]}" ]; then
if [ "${BASH_VERSINFO[0]}" -lt 4 ]; then
echo >&2 "Detected bash version is ${BASH_VERSION}."
echo >&2 "Script needs at least version 4."
err_msg_contact_community
exit 1
fi
else
echo >&2 "There was a problem detecting bash version."
echo >&2 "Aborting because compatibility cannot be detected."
err_msg_contact_community
exit 1
fi
}