#!/usr/bin/ksh -p # # Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved. # . /lib/svc/share/smf_include.sh APACHE_HOME=/usr/apache2/2.2 APACHE_ETC_ROOT=/etc/pkg/depot APACHE_BIN=${APACHE_HOME}/bin/64 # # Calling $APACHE_BIN/apachectl would source $APACHE_BIN/envvars, which # in turn would source /etc/apache2/2.2/envvars, if it exists. # # We want to keep the depot service completely separate from # the site system configuration used by svc:/network/http:apache22 # avoiding /etc/apache2, which means we must avoid apachectl. # # In this method script, we call httpd directly instead, but as a # result, we also need to include the relevant contents of # $APACHE_BIN/envvars - setting $LD_LIBRARY_PATH as it does. # HTTPD=${APACHE_BIN}/httpd.worker LD_LIBRARY_PATH=${APACHE_HOME}/lib/64:${LD_LIBRARY_PATH} export LD_LIBRARY_PATH function check_prop { if [ -z "$1" ]; then echo "ERROR: SMF property for $2 was empty" exit $SMF_EXIT_ERR_FATAL fi } function get_pair { NAME=$1 shift 2 echo "${NAME}=\"$@\"" echo "export ${NAME}" } # A function to pull in the config properties from the FMRI given # as the first argument. This allows us to make a single call # to svcprop, rather than calling svcprop for each value we # want to retrieve. function get_smf_props { IFS=" " SMF_PROPS="$(svcprop -t -p config $1 |\ sed -e 's#config/#depot_#g' -e 's/$/,/g')" IFS=, for line in $SMF_PROPS ; do IFS=' ' eval $(get_pair $line) done } function check_failure { RESULT=$1 MESSAGE=$2 NON_FATAL=$3 if [ $RESULT -ne 0 ]; then echo $MESSAGE if [ -n "$NON_FATAL" ]; then return fi exit $SMF_EXIT_ERR_FATAL fi } function check_apache_failure { RESULT=$1 MESSAGE=$2 if [ $RESULT -ne 0 ]; then echo $MESSAGE kill_apache exit $SMF_EXIT_ERR_FATAL fi } function abspath { # print a normalized version of a path, needed because # many utilities will not dereference non-existent directories # in path names, e.g. cat /etc/no-such-dir/../motd will fail. echo $1 | /usr/bin/python -c \ 'import os.path ; import sys ; print os.path.normpath(sys.stdin.read())' } function run_depot { if [ "${depot_https}" == "true" ]; then https_cmd="--https" smf_fmri_cmd="--smf-fmri ${SMF_FMRI}" if ! [ -z "${depot_ssl_cert_file}" ]; then ssl_cert_file_cmd="--cert ${depot_ssl_cert_file}" fi if ! [ -z "${depot_ssl_key_file}" ]; then ssl_key_file_cmd="--key ${depot_ssl_key_file}" fi if ! [ -z "${depot_ssl_ca_cert_file}" ]; then ssl_ca_cert_file_cmd="--ca-cert ${depot_ssl_ca_cert_file}" fi if ! [ -z "${depot_ssl_ca_key_file}" ]; then ssl_ca_key_file_cmd="--ca-key ${depot_ssl_ca_key_file}" fi if ! [ -z "${depot_ssl_cert_key_dir}" ]; then ssl_cert_key_dir_cmd="--cert-key-dir ${depot_ssl_cert_key_dir}" fi if ! [ -z "${depot_ssl_cert_chain_file}" ]; then ssl_cert_chain_file_cmd="--cert-chain ${depot_ssl_cert_chain_file}" fi fi /usr/lib/pkg.depot-config \ -S \ -c ${depot_cache_dir} \ -h ${depot_host} \ -l ${depot_log_dir} \ -p ${depot_port} \ -r ${depot_runtime_dir} \ -s ${depot_cache_max} \ -T ${depot_template_dir} \ -t apache2 ${https_cmd} \ ${smf_fmri_cmd} \ ${ssl_cert_file_cmd} \ ${ssl_key_file_cmd} \ ${ssl_ca_cert_file_cmd} \ ${ssl_ca_key_file_cmd} \ ${ssl_cert_key_dir_cmd} \ ${ssl_cert_chain_file_cmd} \ ${depot_allow_refresh} failure=$? if [ ${failure} -ne 0 ] ; then # make sure we leave nothing behind kill_apache kill_htcacheclean check_failure $failure \ "pkg.depot-config: failed to create Apache config" fi } function run_htcacheclean { # if we dropped to maintenance and are in the process of # clearing that state, we may have htcacheclean processes # hanging around. kill_htcacheclean if [ "${depot_cache_max}" != "0" ] ; then # Start a cache cleaning daemon, scanning every 2 weeks, # being intelligent about only running if the cache has # changed, limiting the cache to ${depot_cache_max} # megabytes, being nice about scheduling and removing # empty directories if necessary. interval=$((60 * 24 * 14)) /usr/apache2/2.2/bin/htcacheclean \ -d${interval} -i -l ${depot_cache_max}M -n \ -p ${depot_cache_dir} \ -P ${depot_cache_dir}/../depot_htcacheclean.pid \ -t check_failure $? "htcacheclean failed to run cleanly" fi } function kill_htcacheclean { pid_file=$(abspath ${depot_cache_dir}/../depot_htcacheclean.pid) if [ -f $pid_file ]; then PID=$(< $pid_file) /usr/bin/kill -TERM $PID check_failure $? "failed to kill htcacheclean process\ $PID" "not_fatal" fi } function kill_apache { # We go to lengths to kill remaining httpd processes: if we kill # just the pid, then child httpd.worker processes become # zombies, hanging onto the server port, which causes problems # when trying to start a service that is transitioning from # maintenance. # This function should only be called when the service is # transitioning to maintenance: normal Apache shutdown is # preferable. pid_file=$(abspath ${depot_runtime_dir}/../depot_httpd.pid) if [ -f $pid_file ]; then PID=$(< $pid_file) /usr/bin/ptree $PID | /usr/bin/awk '{print $1}' | \ /usr/bin/xargs /usr/bin/kill -TERM check_failure $? "failed to kill apache process $PID" \ "not_fatal" /usr/bin/rm $pid_file fi } function server_ping { # Ping the service, ensuring the index gets built if does # not exist. # Since curl --retry uses an exponential backoff algorithm, this # can result in us waiting 40 seconds, which ought to be long # enough for Apache to come online. (index refreshes are run in # the background on the server after it has returned a response) url="http://${depot_host}:${depot_port}" ipv6=$(echo ${depot_host} | /usr/bin/grep :) if [ -n "$ipv6" ] ; then url="http://\[${depot_host}\]:${depot_port}" fi /usr/bin/curl -s --max-time 5 --retry 4 -o /dev/null ${url} \ 2> /dev/null check_failure $? "Unable to access the server at ${url}. Check\ the SMF service log or the error log at ${depot_log_dir}/error_log for\ more information, if any." } get_smf_props $SMF_FMRI check_prop ${depot_host} config/host check_prop ${depot_port} config/port check_prop ${depot_log_dir} config/log_dir check_prop ${depot_template_dir} config/template_dir check_prop ${depot_runtime_dir} config/runtime_dir check_prop ${depot_cache_dir} config/cache_dir check_prop ${depot_cache_max} config/cache_max check_prop ${depot_https} config/https check_prop ${depot_allow_refresh} config/allow_refresh if [ "${depot_allow_refresh}" == "true" ] ; then depot_allow_refresh="-A" else depot_allow_refresh="" fi FAILED_TO_RUN="Server failed to %s. Check the SMF service log or the\ error log at ${depot_log_dir}/error_log for more information, if any." case "$1" in "start") cmd="start" run_depot # drop privileges now that we've written our configuration /usr/bin/ppriv -s E=basic,net_privaddr $$ run_htcacheclean emsg=$(/usr/bin/printf ${FAILED_TO_RUN} start) ${HTTPD} -f ${depot_runtime_dir}/depot_httpd.conf \ ${STARTUP_OPTIONS} -k ${cmd} 2>&1 check_apache_failure $? $emsg server_ping ;; "refresh") cmd="graceful" run_depot # drop privileges now that we've written our configuration /usr/bin/ppriv -s E=basic,net_privaddr $$ kill_htcacheclean run_htcacheclean emsg=$(/usr/bin/printf ${FAILED_TO_RUN} refresh) ${HTTPD} -f ${depot_runtime_dir}/depot_httpd.conf \ ${STARTUP_OPTIONS} -k ${cmd} 2>&1 check_apache_failure $? $emsg server_ping ;; "stop") cmd="stop" kill_htcacheclean emsg=$(/usr/bin/printf ${FAILED_TO_RUN} stop) # If https service is on and user blindly deleted the certificate dir, # then the stop method will cause error due to not find certificate # and key files. Instead of causing this error, we kill the apache # instance manually. if [[ "${depot_https}" == "true" && \ ! ( -f "${depot_ssl_cert_file}" && \ -f "${depot_ssl_key_file}" ) ]]; then kill_apache else ${HTTPD} -f ${depot_runtime_dir}/depot_httpd.conf \ ${STARTUP_OPTIONS} -k ${cmd} 2>&1 fi check_apache_failure $? $emsg ;; *) echo "Usage: $0 {start|stop|refresh}" exit $SMF_EXIT_ERR_CONFIG ;; esac exit $SMF_EXIT_OK