#!/bin/sh # # Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. . /lib/svc/share/smf_include.sh # Make sure working directory is / to prevent unmounting problems. cd / # Apache configuration files AIWEB_DIR=/var/ai/ai-webserver AIWEB_CONF_DIR=$AIWEB_DIR/conf.d AIWEB_RUNTIME_DIR=/system/volatile/ai-webserver AIWEB_PORTS_CONF=$AIWEB_DIR/compatibility-configuration/ports.conf AIWEB_SSL_KEY_DIR=$AIWEB_DIR/ssl.key AIWEB_SSL_CERT_DIR=$AIWEB_DIR/ssl.crt AIWEB_SSL_CA_CERT_DIR=$AIWEB_DIR/ssl.cacrt AIWEB_COMPAT_CONFIG_DIR=$AIWEB_DIR/compatibility-configuration AI_HTTPD_CONF=$AIWEB_DIR/ai-httpd.conf AI_HTTPD_CONF_TEMPLATE=$AIWEB_DIR/ai-httpd-templ.conf AI_HTTP_PID_FILE=${AIWEB_RUNTIME_DIR}/auto-install_image-server.pid LISTEN_ADDRESSES=$AIWEB_DIR/listen-addresses.conf WEBUI_HTTPD_CONF=$AIWEB_CONF_DIR/wizard.conf WEBUI_ENABLE_HTTPD_CONF=$AIWEB_CONF_DIR/wizard-enabled.conf.in WEBUI_DISABLE_HTTPD_CONF=$AIWEB_CONF_DIR/wizard-disabled.conf.in WEBUI_DOCROOT=/var/ai/image-server/images WEBUI_INDEX_HTML=$WEBUI_DOCROOT/index.html WEBUI_AI_INDEX_HTML=$WEBUI_DOCROOT/ai-index.html # constants WAIT_FOR_PORT_OPEN=10 # Wait for at most 10 seconds for port to open # required commands AIMDNSD=/usr/lib/installadm/aimdnsd.py AIMDNSD_PID=/var/run/aimdnsd APACHE2=/usr/apache2/2.2/bin/apachectl CAT=/usr/bin/cat GREP=/bin/grep KILL=/usr/bin/kill HTTPD=/usr/apache2/2.2/bin/httpd INETD_START_PROP=inetd_start/exec NETBOOTDIR="/etc/netboot" MKDIR=/usr/bin/mkdir LOGGER=/usr/bin/logger PS=/usr/bin/ps PGREP=/usr/bin/pgrep SED=/usr/bin/sed SVCCFG=/usr/sbin/svccfg SVCPROP=/usr/bin/svcprop SVCUTIL=/usr/lib/installadm/svcutil TFTPBOOT=/tftpboot TFTP_SERVICE='svc:/network/tftp/udp6' UPGRADE_SCRIPT=/usr/sbin/installadm-convert NETBOOT_ERR=\ "The ${TFTP_SERVICE} service has been configured to use the ${TFTPBOOT} \ndirectory; The Automated Installer is incompatible with this setting. \nPlease use svccfg to change the ${INETD_START_PROP} property of the \n${TFTP_SERVICE} service to migrate to the ${NETBOOTDIR} directory.\n" FIND=/usr/bin/find # Automated Install SMF variables PORT='all_services/port' SECURE_PORT='all_services/secure_port' FILES='all_services/webserver_files_dir' SECURE_FILES='all_services/webserver_secure_files_dir' ENABLE_WEBUI='all_services/enable_webui' DEBUG_PROP='install/debug' REFRESH_TIMEOUT_PROP='refresh/timeout_seconds' # Global SMF property values SMF_PORT=$($SVCPROP -p $PORT $SMF_FMRI 2>/dev/null) SMF_SECURE_PORT=$($SVCPROP -p $SECURE_PORT $SMF_FMRI 2>/dev/null) WEBSRVR_FILES=$($SVCPROP -p $FILES $SMF_FMRI 2>/dev/null) WEBSRVR_SECURE_FILES=$($SVCPROP -p $SECURE_FILES $SMF_FMRI 2>/dev/null) SMF_ENABLE_WEBUI=$($SVCPROP -p $ENABLE_WEBUI $SMF_FMRI 2>/dev/null) SMF_REFRESH_TIMEOUT=$($SVCPROP -p $REFRESH_TIMEOUT_PROP $SMF_FMRI 2>/dev/null) export PYLOG_LEVEL=$($SVCPROP -p $DEBUG_PROP $SMF_FMRI 2>/dev/null) # # setup_main_ports # Sets up the listen_address file with the ip addresses and SMF port # needed for the services. The ${AI_HTTPD_CONF} file contains a line # that includes the ${LISTEN_ADDRESSES} file which this function creates. # # Args # None # Globals # SMF_PORT - SMF port number. # Returns # 0 - New ${LISTEN_ADDRESSES} file has been put into place. # 1 - Existing ${LISTEN_ADDRESSES} file was not touched. # function setup_main_ports { $SVCUTIL create-main-ports-file ${SMF_PORT} return $? } # # setup_webserver_files_dir # Sets up the all_services/webserver_files_dir and # all_services/webserver_secure_files_dir directory. # # Args # $1 = 'secure' then setup the secure files otherwise # setup the non-secure files # Globals # WEBSRVR_FILES - the SMF property that contains the # all_services/webserver_files_dir value # WEBSRVR_SECURE_FILES - the SMF property that contains the # all_services/webserver_secure_files_dir value # # Returns # 0 - File created # 1 - no DocumentRoot in ai_httpd.conf --OR-- # unable to remove original link --OR-- # unable to create new link # function setup_webserver_files_dir { if [[ $# != 0 && $1 == 'secure' ]]; then $SVCUTIL setup-webserver-secure-files-dir \ "${WEBSRVR_SECURE_FILES}" else $SVCUTIL setup-webserver-files-dir "${WEBSRVR_FILES}" fi return $? } function ensure_ports_updated { echo "Updating the ports to ${SMF_PORT}." # Placing the refresh-ports into the background to avoid # the SMF service timeout as this can take quite a bit # of time for a large number of services. $SVCUTIL refresh-ports ${SMF_PORT} & } # # verify_apache_restart # Verify if apache server needs a graceful restart due to creation/ # removal/modification of config files. If any of the known list of # config paths is newer than $AI_HTTP_PID_FILE, a graceful restart is # required. # # Args # None # Return: # 0 - Newer apache configuration found, server needs a restart # 1 - Apache server configuration up-to-date, no need to restart # function verify_apache_restart { if [[ ! -f $AI_HTTP_PID_FILE ]]; then return 0 fi set -A files_to_check $AI_HTTPD_CONF \ $LISTEN_ADDRESSES \ $AIWEB_PORTS_CONF # Deletion of creds is checked for by including the relevant dirs. set -A dirs_to_check $AIWEB_SSL_KEY_DIR \ $AIWEB_SSL_CERT_DIR \ $AIWEB_SSL_CA_CERT_DIR \ $AIWEB_CONF_DIR \ $AIWEB_COMPAT_CONFIG_DIR # Use file name generation to return only matching files which exist # when this script is executed. set -A wildcards_to_check ~(N)$AIWEB_DIR/ssl.*/* \ ~(N)$AIWEB_CONF_DIR/*.conf set -A paths_to_check ${files_to_check[*]} ${dirs_to_check[*]} \ ${wildcards_to_check[*]} # Check if any of the paths to check are newer than the pid file. # Use -prune to prevent find examining sub-dirs of dirs_to_check set -A newer_files \ $($FIND ${paths_to_check[*]} -prune \ -newer $AI_HTTP_PID_FILE 2>/dev/null) if [[ ${#newer_files[@]} -gt 0 ]] ; then return 0 fi return 1 } # # setup_webui # Ensures correct webui http conf file is in place to enable/disable access # # Args # None # Globals # SMF_ENABLE_WEBUI - SMF property indicating web ui availability # Returns # 0 - Webui enable/disable success # 1 - Webui enable/disable failure # function setup_webui { retval=1 if [ "${SMF_ENABLE_WEBUI}" == "true" ]; then # Ensure webui conf file is in place to enable access conftouse=$WEBUI_ENABLE_HTTPD_CONF webui_disabled=0 else # Ensure webui conf file is in place to disable access conftouse=$WEBUI_DISABLE_HTTPD_CONF webui_disabled=1 fi # Remove index.html if webui is enabled, re-create link rm -f $WEBUI_INDEX_HTML if [ $webui_disabled -eq 0 ]; then ln -s $WEBUI_AI_INDEX_HTML $WEBUI_INDEX_HTML fi # Check if conf file to use is different than current link diff $WEBUI_HTTPD_CONF $conftouse >/dev/null 2>&1 if [ $? -ne 0 ]; then rm -f $WEBUI_HTTPD_CONF if [ -e $conftouse ]; then ln -s $conftouse $WEBUI_HTTPD_CONF fi retval=0 fi return $retval } # # test_connect_port # Attempts to connect to the specified port on localhost # # Args # $1 = the port number to connect to # Globals # None # Returns # 0 - Successfully connect to specified port # 1 - Failed to connect to the specified port # function test_connect_port { if [ -n "$1" ]; then (: < /dev/tcp/localhost/${1}) 2>/dev/null return $? fi return 1 } # # start_apache # Starts the apache web server, waits until the PID file has # been created, or a timeout reached # # Args # $1 - The apache config file to be used # Globals # AI_HTTP_PID_FILE - The apache PID file # Returns # 0 - Successfully started apache # 1 - Failed to start apache # function start_apache { if [ -z "$1" ]; then return 1 fi if [ ! -d ${AIWEB_RUNTIME_DIR} ] ; then ${MKDIR} ${AIWEB_RUNTIME_DIR} fi attempt=3 while [[ ! -e ${AI_HTTP_PID_FILE} && $attempt -gt 0 ]]; do attempt=$(( $attempt - 1 )) echo "Starting Apache webserver" ${APACHE2} -f $1 -k start if [[ $? -ne 0 ]]; then return 1 fi # Wait until it's really started, i.e. the PID file exists typeset -i timeout=30 while [[ ! -e ${AI_HTTP_PID_FILE} && $timeout -gt 0 ]]; do timeout=$(( $timeout - 1 )) sleep 1 done done if [[ ! -e ${AI_HTTP_PID_FILE} ]]; then return 1 fi echo "Apache start completed" return 0 } SVC_ACTION_SUCCESS=0 SVC_ACTION_FAILED=1 SVC_ACTION_PARTIAL_FAILURE=2 SVC_BUSY=3 # # invoke_ai_server # # Invoke an action on AI Server # A retcode of SVC_ACTION_PARTIAL_FAILURE from svcutil will cause a syslog # message to be generated, but the operation will be considered as successful. # # Arg # $1 - svcutil command # # Returns # 0 - Successfully invoke command. One or more partial failures against # a service may have occurred. # 1 - Operation failed # function invoke_ai_server { if [ -z "$1" ]; then return 1 fi $SVCUTIL $1 retcode=$? if [ $retcode -eq ${SVC_ACTION_SUCCESS} ]; then return 0 fi if [ $retcode -eq ${SVC_ACTION_PARTIAL_FAILURE} ]; then # Generate a syslog message to inform the admin that # some failures occurred ${LOGGER} -p daemon.warning -t ${SMF_FMRI} \ "One or more AI services encountered an error while" \ "performing a $1 operation on the image associated with" \ "the service, see SMF log for details." # On partial failures we want the AI Server action to be seen # as successful and not go into maintenance mode return 0 fi # We got a SVC_BUSY or SVC_ACTION_FAILED return 1 } # # wait_for_port # Loops until the specified port is opened, or a timeout reached # # Args # $1 - the port number to connect to # $2 - (Optional) how long to wait in seconds # Globals # None # Returns # 0 - Successfully connected to port before timeout # 1 - Failed to connect to port, or invalid port # function wait_for_port { if [ -z "$1" ]; then return 1 fi typeset timeout="false" if [ -n "$2" ]; then timeout="$2" fi # The assumption is that this test is quick and sleeping 1 second is # reasonable in relation to the overall timeout. while [[ $timeout == "false" || $timeout -gt 0 ]]; do if test_connect_port "$1"; then break fi sleep 1 if [[ $timeout != "false" ]]; then timeout=$(( $timeout - 1 )) fi done } if [ ! -e $AI_HTTPD_CONF ]; then echo "Creating $AI_HTTPD_CONF as a symbolic link." ln -s $AI_HTTPD_CONF_TEMPLATE $AI_HTTPD_CONF fi case "$1" in 'start') # Code to execute on start # Ensure that no old, incompatible services exist old_services=$(${SVCCFG} -s ${SMF_FMRI} listpg | ${GREP} "^AI" | ${SED} 's#AI\(.*\) .*#\1#') if [ ! -z "$old_services" ]; then echo "The following services are incompatible with this" echo "version of $SMF_FMRI:" echo $old_services echo "Please boot Solaris 11 11/11, run $UPGRADE_SCRIPT" echo "and then rerun the Solaris upgrade" exit $SMF_EXIT_ERR_FATAL fi # Ensure that tftp/udp6 is setup to use /etc/netboot start_exec_prop=$(${SVCPROP} -p ${INETD_START_PROP} ${TFTP_SERVICE}) inetd_start=$(echo ${start_exec_prop} |awk '{ print $NF }') if [ $inetd_start != ${NETBOOTDIR} ]; then if [[ -d ${TFTPBOOT} && -n $(ls ${TFTPBOOT}) ]]; then echo ${NETBOOT_ERR} exit $SMF_EXIT_ERR_FATAL fi fi # ensure that the default port is set if [ "X${SMF_PORT}" == "X" ]; then echo "The default port is not set for the service." exit ${SMF_EXIT_ERR_CONFIG} fi # Double check that the ports within the services match what # the service properties are set with. ensure_ports_updated # First run aimdns daemon to register mDNS records for every # service that is listed as enabled. if [ -f ${AIMDNSD_PID} ] ; then # only if aimdns daemon is not running do we start another if [[ ! "$($PS -p $($CAT $AIMDNSD_PID) -o args=)" == \ ~(E)"$AIMDNSD\$" ]]; then $AIMDNSD & fi else # no PID file found start aimdns daemon $AIMDNSD & fi # Ensure Web UI is enabled/disabled as configured setup_webui # Start up the apache web server using our http config file if [ -f ${AI_HTTPD_CONF} ] ; then setup_main_ports setup_webserver_files_dir setup_webserver_files_dir 'secure' start_apache ${AI_HTTPD_CONF} if [ $? -ne 0 ] ; then echo "Unable to start apache process" exit $SMF_EXIT_ERR_CONFIG fi wait_for_port ${SMF_PORT} ${WAIT_FOR_PORT_OPEN} else echo "Unable to start apache process due to missing" \ "config file ${AI_HTTPD_CONF}" exit $SMF_EXIT_ERR_CONFIG fi invoke_ai_server remount || exit $SMF_EXIT_ERR_FATAL ;; 'stop') # Code to execute on stop # stop aimdns daemon if [ -f ${AIMDNSD_PID} ] ; then ${KILL} $($CAT $AIMDNSD_PID) fi # stop apache server if [ -f ${AI_HTTPD_CONF} ] ; then ${APACHE2} -f ${AI_HTTPD_CONF} -k stop if [ $? -ne 0 ] ; then echo "Unable to stop apache process" exit $SMF_EXIT_ERR_CONFIG fi # wait for the apache servers to exit or the stop timeout is reached while [[ $($PGREP -f "${HTTPD} -f ${AI_HTTPD_CONF}") ]]; do sleep 1 done else echo "Unable to stop apache process due to missing" \ "config file ${AI_HTTPD_CONF}" exit $SMF_EXIT_ERR_CONFIG fi invoke_ai_server unmount-all || exit $SMF_EXIT_ERR_FATAL ;; 'refresh') # ensure that the default port is set if [ "X${SMF_PORT}" == "X" ]; then echo "The default port is not set for the service." exit ${SMF_EXIT_ERR_CONFIG} fi # Double check that the ports within the services match what # the service properties are set with. ensure_ports_updated # if the aimdns daemon is running, refresh it, otherwise start it up. if [ -f ${AIMDNSD_PID} ] ; then ${KILL} -1 $($CAT $AIMDNSD_PID) else $AIMDNSD & fi # Mount services in /etc/netboot as needed invoke_ai_server remount || exit $SMF_EXIT_ERR_FATAL setup_main_ports typeset -i ret1=$? setup_webserver_files_dir typeset -i ret2=$? setup_webserver_files_dir 'secure' typeset -i ret3=$? # Check if restart required, e.g. conf file changes verify_apache_restart typeset -i ret4=$? setup_webui typeset -i ret5=$? if [[ $ret1 -eq 0 || $ret2 -eq 0 || $ret3 -eq 0 || $ret4 -eq 0 || $ret5 -eq 0 ]] ; then if [[ $ret1 -eq 0 ]] ; then print -n "Port configuration has changed, " fi if [[ $ret2 -eq 0 || $ret3 -eq 0 || $ret4 -eq 0 || $ret5 -eq 0 ]] ; then print -n "Files configuration has changed, " fi # Apache doesn't like graceful restarts occurring to quickly, so # we try to wait until it stabilizes before completing the SMF action. # # To determine whether Apache has stabilized, we are relying on the # fact that it touches/updates the PID file when it has reached that # point. # # Create a temp file to compare its timestamp against the Apache PID # file timestamp. # BEFORE_RESTART=$(mktemp -p ${AIWEB_RUNTIME_DIR} .ai-webserver.before-XXXXX) echo "refreshing webserver." ${APACHE2} -f ${AI_HTTPD_CONF} -k graceful # We wait for Apache to refresh its PID file to know when it # has completed restarting, and then wait for the port to be # available. # # We stop waiting about 60 seconds before the configured # refresh method timeout so that we don't risk being killed by # SMF prematurely. typeset -i timeout=$(($SMF_REFRESH_TIMEOUT - 60)) typeset -i wait_for_pid_timeout=0 typeset -i wait_for_port_timeout=0 if [[ $timeout -gt ${WAIT_FOR_PORT_OPEN} ]]; then wait_for_pid_timeout=$(( $timeout - ${WAIT_FOR_PORT_OPEN})) wait_for_port_timeout=${WAIT_FOR_PORT_OPEN} elif [[ $timeout -gt 0 ]]; then wait_for_pid_timeout=$timeout fi while [[ $wait_for_pid_timeout -gt 0 && ( ! -f ${AI_HTTP_PID_FILE} || \ ${BEFORE_RESTART} -nt ${AI_HTTP_PID_FILE} ) ]]; do # The assumption is that this test is quick and # sleeping 1 second is reasonable in relation to the # overall timeout. sleep 1 wait_for_pid_timeout=$(( $wait_for_pid_timeout - 1 )) done # Try to wait for the port to be available wait_for_port ${SMF_PORT} ${wait_for_port_timeout} if [[ -n ${BEFORE_RESTART} ]]; then rm -f ${BEFORE_RESTART} fi else echo "Port and files configuration has not changed, " \ "not refreshing webserver." fi ;; *) echo "Usage: $0 { start | stop | refresh }" exit 1 ;; esac exit $SMF_EXIT_OK