#!/bin/bash # # stap-server script for managing the systemtap compile server # # Copyright (C) 2008, 2009 Red Hat Inc. # # This file is part of systemtap, and is free software. You can # redistribute it and/or modify it under the terms of the GNU General # Public License (GPL); either version 2, or (at your option) any # later version. # # This script provides management of systemtap compile servers as a service. # See stap-server(8) for more information. . /etc/rc.d/init.d/functions # Systemtap function library . ${PKGLIBEXECDIR}stap-env prog=stap-server # Commands STAP_START_SERVER=${stap_pkglibexecdir}stap-start-server STAP_STOP_SERVER=${stap_pkglibexecdir}stap-stop-server UNAME=/bin/uname # Default Global Configuration CONFIG_FILE=$stap_sysconfdir/sysconfig/stap-server CONFIG_PATH=$stap_sysconfdir/stap-server/conf.d STAT_PATH=$stap_localstatedir/run/stap-server LOG_FILE=$stap_localstatedir/log/stap-server/log # Default option settings # Optional global config file OPT_CONFIG_FILE= echo_usage () { echo $"Usage: $prog {start|stop|restart|condrestart|try-restart|force-reload|status} [options]" echo $"Options:" echo $" -c configfile : specify additional global configuration file." echo $" -a arch : specify the target architecture." echo $" -r release : specify the kernel release." echo $" -I path : augment the search path for tapsets." echo $" -R path : specify the location of the systemtap runtime." echo $" -B options : specify 'make' options for building systemtap modules." echo $" -u username : specify the user who will run the server(s)." echo $" -i : specify a server for each installed kernel release." echo $" -n nickname : specify a server configuration by nickname." echo $" -p pid : specify a server or server configuration by process id." echo $"" echo $"All options, may be specified more than once." echo $"" echo $"If -a is not specified, the default architecture is that of the host" echo $"platform." echo $"" echo $"If -r is not specified, the default kernel release is that currently" echo $"running on the host platform." echo $"" echo $"If -u is not specified, the default user is 'stap-server'" echo $"" echo $"Each -I and -B option specifies an additional path or option" echo $"respectively. For other options, each new instance overrides the" echo $"previous setting." echo $"" echo $"The -i option is a shortcut which specifies one server for each kernel" echo $"release installed in /lib/modules/. Previous -I, -R, -B and -u" echo $"options will be applied to each server, however previous -a options" echo $"are ignored and the default architecture is used." echo $"" echo $"The -n option allows the specification of a server configuration by" echo $"nickname. When -n is specified, a currently running server with the" echo $"given nickname will be searched for. If no currently running server" echo $"with the given nickname is found, a server configuration with the" echo $"given nickname will be searched for in $stap_sysconfdir/stap-server/conf.d/*.conf." echo $"If a server configuration for the given nickname is found, the -a, -r," echo $"-I, -R, -B and -u options for that server will be used as if they were" echo $"specified on the command line. If no configuration with the given" echo $"nickname is found, and the action is 'start' (or an action behaving" echo $"like 'start' (see below), the server will be started with the given" echo $"nickname. If no configuration with the given nickname is found, and" echo $"the action is not 'start' (or an action behaving" "like 'start'," echo $"it is an error. If a nickname is not specified for a server, its" echo $"nickname will be its process id." echo $"" echo $"The -p option allows the specification of a server configuration by" echo $"process id. When -p is specified, a currently running server with the" echo $"given process id will be searched for. If no such server is found," echo $"it is an error. If a server with the given pid is found, the -a, -r," echo $"-I, -R, -B and -u options for that server will be used as if they were" echo $"specified on the command line." echo $"" echo $"The specified action is performed for the server(s) specified on the" echo $"command line. If no servers are specified on the command line, the" echo $"behavior is as follows:" echo $"" echo $" start: Start the servers configured in $stap_sysconfdir/stap-server/conf.d/*.conf." echo $" If none are configured, start a server for the kernel release" echo $" and architecture of the host platform." echo $"" echo $" stop: Stop all currently running servers." echo $"" echo $" restart: Restart all currently running servers. If no servers are running," echo $" behave as if 'start' was specified." echo $"" echo $" condrestart: Restart all currently running servers. If no servers are running," echo $" do nothing." echo $"" echo $" try-restart: Same as condrestart." echo $"" echo $" force-reload: Stop all currently running servers and behave as if 'start'" echo $" was specified." echo $"" echo $" status: Report the status of all current running servers." echo $"" } #----------------------------------------------------------------- # Helper functions #----------------------------------------------------------------- log () { # message echo `LC_ALL=en date +"%b %e %T"`": $1" >> "$LOG_FILE" } clog () { # message [-n] echo $2 "$1" log "$1" } slog () { # message logger "$1" # if syslogd is running, this message will be sent to syslog. log "$1" } logex () { # command eval log \"Exec: $@\" "$@" >> "$LOG_FILE" 2>&1 return $? } do_failure () { # message slog "Error: $1" failure "$1" } do_success () { # message log "Pass: $1" success "$1" } #------------------------------------------------------------------ # Parameter parsing and setup options #------------------------------------------------------------------ parse_args () { # arguments local rc=0 while [ -n "$1" ]; do case "$1" in -a) OPT_SERVER_CMDS="$OPT_SERVER_CMDS ARCH='$2'" shift 1 ;; -B) OPT_SERVER_CMDS="$OPT_SERVER_CMDS BUILD='$2'" shift 1 ;; -c) OPT_CONFIG_FILE=$2 shift 1 ;; -i) process_i ;; -I) OPT_SERVER_CMDS="$OPT_SERVER_CMDS INCLUDE='$2'" shift 1 ;; -n) process_n $2 shift 1 ;; -p) process_p $2 test $? = 0 || rc=1 shift 1 ;; -r) process_r $2 test $? = 0 || rc=1 shift 1 ;; -R) OPT_SERVER_CMDS="$OPT_SERVER_CMDS RUNTIME='$2'" shift 1 ;; -u) OPT_SERVER_CMDS="$OPT_SERVER_CMDS USER='$2'" shift 1 ;; --) ;; *) rc=1 ;; esac shift 1 done # Add an EXEC command to the end if any server options were specified test -n "$OPT_SERVER_CMDS" && OPT_SERVER_CMDS="$OPT_SERVER_CMDS EXEC" test $rc != 0 && echo_usage return $rc } # Process the -i flag. process_i () { cd /lib/modules local release for release in `ls`; do test -n "$OPT_SERVER_CMDS" && OPT_SERVER_CMDS="$OPT_SERVER_CMDS EXEC" process_r $release done return 0 } # Process the -n flag. process_n () { local target_NICKNAME="$1" # Is there a running server with this nickname? local pid=`get_server_pid_by_nickname "$target_NICKNAME"` if [ -n "$pid" ]; then # Read the configuration and add it to the configuration commands. . $STAT_PATH/$pid OPT_SERVER_CMDS="$OPT_SERVER_CMDS `echo_server_commands`" return fi # Is there a server configuration with this nickname? for f in "$CONFIG_PATH"/*.conf; do if [ -f "$f" ]; then . "$f" test "X$NICKNAME" = "X$target_NICKNAME" || continue OPT_SERVER_CMDS="$OPT_SERVER_CMDS `echo_server_commands`" return fi done # No server configuration could be found for this nickname. Add a # NICKNAME_NOT_FOUND=... command to the configuration commands. OPT_SERVER_CMDS="$OPT_SERVER_CMDS NICKNAME_NOT_FOUND='$target_NICKNAME'" } # Process the -p flag. process_p () { local pid="$1" # Are we managing a server with the given pid? test ! -f $STAT_PATH/$pid && echo "No stap-server running as pid $pid" && \ exit 1 # Add the configuration of the server running as $pid to OPT_SERVER_CMDS . $STAT_PATH/$pid OPT_SERVER_CMDS="$CONFIG_SERVER_CMDS `echo_server_commands`" return 0 } # Process the -r flag. process_r () { local first_char=`expr "$1" : '\(.\).*'` if test "$first_char" = "/"; then # fully specified path local kernel_build_tree=$1 local version_file_name="$kernel_build_tree/include/config/kernel.release" # The file include/config/kernel.release within the kernel # build tree is used to pull out the version information local kernel_release=`cat $version_file_name 2>/dev/null` if test "X$kernel_release" = "X"; then echo "Missing $version_file_name" return 1 fi OPT_SERVER_CMDS="$OPT_SERVER_CMDS RELEASE='$kernel_release'" return 0 fi # kernel release specified directly OPT_SERVER_CMDS="$OPT_SERVER_CMDS RELEASE='$1'" return 0 } load_config () { # Include configs if [ -f "$CONFIG_FILE" ]; then . "$CONFIG_FILE" fi if [ -f "$OPT_CONFIG_FILE" ]; then . "$OPT_CONFIG_FILE" fi } # Default to the currently running kernel release get_release () { $UNAME -r } # Default to the currently running kernel release get_arch () { stap_get_arch } echo_server_commands () { # Echo the configuration command string. echo -n "ARCH='$ARCH'" echo -n " RELEASE='$RELEASE'" echo -n " RUNTIME='$RUNTIME'" for i in $INCLUDE; do echo -n " INCLUDE='$i'" done for b in $BUILD; do echo -n " BUILD='$b'" done echo -n " USER='$USER'" echo -n " NICKNAME='$NICKNAME'" echo } echo_server_options () { # Echo the configuration options. echo -n "-a '$ARCH'" echo -n " -r '$RELEASE'" test -n "$RUNTIME" && echo -n " -R '$RUNTIME'" for i in $INCLUDE; do echo -n " -I '$i'" done for b in $BUILD; do echo -n " -B '$b'" done test -n "$USER" && echo -n " -u '$USER'" echo -n " -n '$NICKNAME'" echo } load_server_config () { CONFIG_SERVER_CMDS= for f in "$CONFIG_PATH"/*.conf; do if [ -f "$f" ]; then # Obtain a configuration from each config file. # Ensure that we get the correct defaults for items not specified. local ARCH= local BUILD= local INCLUDE= local RUNTIME= local USER= local RELEASE= . "$f" # Other options default to empty. These ones don't. [ -z "$ARCH" ] && ARCH=`get_arch` [ -z "$RELEASE" ] && RELEASE=`get_release` [ -z "$USER" ] && USER=$STAP_USER CONFIG_SERVER_CMDS="$CONFIG_SERVER_CMDS `echo_server_commands` EXEC" fi done } prepare_stat_dir () { if [ ! -d "$STAT_PATH" ]; then logex mkdir -p "$STAT_PATH" [ $? -ne 0 ] && return 1 fi return 0 } prepare_log_dir () { local log_path=`dirname "$LOG_FILE"` if [ ! -d "$log_path" ]; then mkdir -p "$log_path" [ $? -ne 0 ] && return 1 fi return 0 } stat_file () { # server-spec echo $STAT_PATH/$1 } default_server_cmds () { echo "EXEC" } init_server_opts () { ARCH=`get_arch` RELEASE=`get_release` BUILD= INCLUDE= NICKNAME= NICKNAME_NOT_FOUND= RUNTIME= USER=$STAP_USER } server_still_running () { # PID (ps -e | grep stap-serverd | grep -q $1) && return 0 # Still running rm -f $STAT_PATH/$1 return 1 # Not running } get_server_pid_by_config () { # Need to save the config, since the process of checking the running # servers alters it. local target_ARCH="$ARCH" local target_RELEASE="$RELEASE" local target_INCLUDE="$INCLUDE" local target_RUNTIME="$RUNTIME" local target_BUILD="$BUILD" local target_USER="$USER" local target_NICKNAME="$NICKNAME" # Check the status file for each running server to see if it matches # the one currently configured. We're checking for a given configuration, # so don't compare the nickname. for f in $STAT_PATH/*; do test ! -e $f && continue . $f test "X$ARCH" = "X$target_ARCH" || continue test "X$RELEASE" = "X$target_RELEASE" || continue test "X$INCLUDE" = "X$target_INCLUDE" || continue test "X$RUNTIME" = "X$target_RUNTIME" || continue test "X$BUILD" = "X$target_BUILD" || continue test "X$USER" = "X$target_USER" || continue echo `basename $f` # Server has a pid return done ARCH="$target_ARCH" RELEASE="$target_RELEASE" INCLUDE="$target_INCLUDE" RUNTIME="$target_RUNTIME" BUILD="$target_BUILD" USER="$target_USER" NICKNAME="$target_NICKNAME" } get_server_pid_by_nickname () { # No need to save the current configuration. This function is not called # in a context requiring it. local target_NICKNAME="$1" # Check the status file for each running server to see if the nickname # matches the one we want. for f in $STAT_PATH/*; do test ! -e $f && continue . $f test "X$NICKNAME" = "X$target_NICKNAME" || continue echo `basename $f` # Server with nickname was found return done } managed_servers () { if [ ! -d $STAT_PATH ]; then echo "" return 1 fi cd $STAT_PATH local list=`ls` if [ -z "$list" ]; then echo "" return 1 fi echo "$list" } eval_server_command () { local cmd="$1" # Accumulate the results of BUILD and INCLUDE commands. if echo $cmd | grep -q ^BUILD; then local prevBUILD="$BUILD" eval $cmd BUILD="$prevBUILD $BUILD" BUILD=`echo $BUILD | sed 's/^ //'` elif echo $cmd | grep -q ^INCLUDE; then local prevINCLUDE="$INCLUDE" eval $cmd INCLUDE="$prevINCLUDE $INCLUDE" INCLUDE=`echo $INCLUDE | sed 's/^ //'` else eval $cmd fi } start_server () { clog $"Starting $prog for $RELEASE $ARCH: " -n # Is there already a server running for the requested kernel release # and arch? local server_pid=`get_server_pid_by_config` if test -n "$server_pid"; then if server_still_running $server_pid; then do_success $"$prog start `echo_server_options`" return 0 # Success fi fi # Construct the server start command. local server_cmd="$STAP_START_SERVER -r '$RELEASE' -a '$ARCH'" for b in $BUILD; do server_cmd="$server_cmd -B '$b'" done for i in $INCLUDE; do server_cmd="$server_cmd -I '$i'" done test -n "$RUNTIME" && server_cmd="$server_cmd -R '$RUNTIME'" server_cmd="$server_cmd --log=$LOG_FILE" # Start the server here. local pid if [ -n "$USER" ]; then pid=`runuser -s /bin/bash - $USER -c "$server_cmd"` else pid=`eval $server_cmd` fi if [ $? != 0 -o -z "$pid" ]; then if [ -n "$pid" ]; then rm -f $STAT_PATH/$pid fi do_failure $"$prog start `echo_server_options`" return 1 # Failure fi # Nickname defaults to the pid. test -z "$NICKNAME" && NICKNAME="$pid" # Write the configuration to the status file. local server_status_file=$STAT_PATH/$pid echo "ARCH='$ARCH'" > $server_status_file echo "USER='$USER'" >> $server_status_file echo "BUILD='$BUILD'" >> $server_status_file echo "INCLUDE='$INCLUDE'" >> $server_status_file echo "NICKNAME='$NICKNAME'" >> $server_status_file echo "RUNTIME='$RUNTIME'" >> $server_status_file echo "RELEASE='$RELEASE'" >> $server_status_file do_success $"$prog start `echo_server_options`" } start () { # server-cmds prepare_stat_dir if [ $? -ne 0 ]; then do_failure $"Failed to make stat directory ($STAT_PATH)" return 1 fi # Start the specified servers server_cmds="$1" # If none specified, start the configured servers if [ -z "$server_cmds" ]; then load_server_config server_cmds="$CONFIG_SERVER_CMDS" # If none configured, start the default servers [ -z "$server_cmds" ] && server_cmds=`default_server_cmds` fi # Start each requested server in turn local rc=0 local first=1 init_server_opts local cmd local prevCmd for cmd in $server_cmds; do prevCmd=$cmd # Evaluate commands until the EXEC command is found. if test "$cmd" != "EXEC"; then eval_server_command $cmd # A specified nickname only sticks if it is the final command. # Otherwise, we have a configuration based on a nicknamed # configuration. echo "$cmd" | grep -q "^NICKNAME=" || NICKNAME="" continue fi # If a nickname was specified, but the corresponding config was not found, # then it is the nickname for this new configuration. if test -n "$NICKNAME_NOT_FOUND"; then NICKNAME="$NICKNAME_NOT_FOUND" NICKNAME_NOT_FOUND= fi # Start the configured server test $first = 0 && echo first=0 start_server || rc=1 # Don't use the same nickname for the next server. NICKNAME= done return $rc } stop () { # server-cmds local first=1 local server_list= server_cmds="$1" if [ -n "$server_cmds" ]; then # Get the pids of all the requested servers. init_server_opts local cmd local prevCmd for cmd in $server_cmds; do prevCmd=$cmd # Evaluate commands until the EXEC command is found. if test "$cmd" != "EXEC"; then eval_server_command $cmd # A specified nickname only sticks if it is the final command. # Otherwise, we have a configuration based on a nicknamed # configuration. echo "$cmd" | grep -q "^NICKNAME=" || NICKNAME="" continue fi # If a nickname was specified, but the corresponding config was not # found, it is an error. if test -n "$NICKNAME_NOT_FOUND"; then clog "No configuration found for the nickname '$NICKNAME_NOT_FOUND'" -n NICKNAME="$NICKNAME_NOT_FOUND" do_failure $"$prog stop `echo_server_options`" NICKNAME_NOT_FOUND= rc=1 continue fi # Get the pid for this server, if it's running local server_pid=`get_server_pid_by_config` if test -n "$server_pid"; then server_list="$server_list $server_pid" continue fi # This server is not running, but give a success stop status anyway. test $first = 0 && echo first=0 clog $"Stopping $prog for $RELEASE $ARCH: " -n do_success $"$prog stop `echo_server_options`" done else server_list=`managed_servers` if [ -z "$server_list" ]; then clog $"Stopping $prog: " -n do_success $"$prog: No managed servers to stop" return 0 fi fi # Stop each server in turn local rc=0 local pid for pid in $server_list; do . $STAT_PATH/$pid test $first = 0 && echo first=0 clog $"Stopping $prog for $RELEASE $ARCH: " -n local this_rc=0 if server_still_running $pid; then if [ -n "$USER" ]; then runuser -s /bin/bash - $USER -c "$STAP_STOP_SERVER $pid" else eval $STAP_STOP_SERVER $pid fi if [ $? != 0 ]; then do_failure $"$prog stop `echo_server_options`" this_rc=1 rc=1 fi fi if [ $this_rc = 0 ]; then rm -f $STAT_PATH/$pid do_success $"$prog stop `echo_server_options`" fi done return $rc } status () { # server-list local rc=0 # Report status for the specified servers or all running servers, if none # specified. local server_list= server_cmds="$1" if [ -n "$server_cmds" ]; then # Get the pids of all the requested servers. init_server_opts local cmd local prevCmd for cmd in $server_cmds; do prevCmd=$cmd # Evaluate commands until the EXEC command is found. if test "$cmd" != "EXEC"; then eval_server_command $cmd # A specified nickname only sticks if it is the final command. # Otherwise, we have a configuration based on a nicknamed # configuration. echo "$cmd" | grep -q "^NICKNAME=" || NICKNAME="" continue fi # If a nickname was specified, but the corresponding config was not # found, say so. if test -n "$NICKNAME_NOT_FOUND"; then echo "No configuration found for the nickname '$NICKNAME_NOT_FOUND'" NICKNAME_NOT_FOUND= rc=3 continue fi # Get the pid for this server, if it's running local server_pid=`get_server_pid_by_config` if test -n "$server_pid"; then server_list="$server_list $server_pid" continue fi # This server is not running echo "stap-server `echo_server_options`" rc=3 done else server_list=`managed_servers` if [ -z "$server_list" ]; then echo "No managed stap-server is running" return 3 fi fi # Get status of each server in turn local pid for pid in $server_list; do . $STAT_PATH/$pid if ! server_still_running $pid; then echo "stap-server `echo_server_options` started as PID $pid is no longer running" rc=1 continue fi echo "stap-server `echo_server_options` running as PID $pid" done return $rc } # Restart or start if not running function restart () { # server-cmds # Restart the specified servers or all servers, if none specified. local server_cmds="$1" if [ -z "$server_cmds" ]; then local server_list=`managed_servers` local pid for pid in $server_list; do . $STAT_PATH/$pid server_cmds="$server_cmds `echo_server_commands` EXEC" done fi # Stop the specified servers, or all if none specified stop "$server_cmds" local rc=$? echo # Restart the same servers. If none were specified then # start the configured or default server(s)). start "$server_cmds" local this_rc=$? [ $this_rc != 0 ] && rc=$this_rc return $rc } # Restart only if running function condrestart () { # server-list # Restart the specified servers or all servers, if none specified, # but only if they are already running. local server_cmds="$1" if [ -z "$server_cmds" ]; then local server_list=`managed_servers` local pid for pid in $server_list; do . $STAT_PATH/$pid server_cmds="$server_cmds `echo_server_commands` EXEC" done # No server specified or running? if [ -z "$server_cmds" ]; then clog "No managed stap-server is running" -n do_success "No managed stap-server is running" return 0 fi fi # For each server in the list, stop it if it is running local start_cmds= local first=1 local server_cmd= local cmd for cmd in $server_cmds; do # Execute and collect commands until the EXEC command is found. server_cmd="$server_cmd $cmd" if test "$cmd" != "EXEC"; then eval_server_command $cmd continue fi test $first = 0 && echo first=0 # Now see if this server is running if ! status "$server_cmd" >/dev/null 2>&1; then clog $"$prog for $RELEASE $ARCH is not running" -n do_success "$prog `echo_server_options` is not running" server_cmd= continue fi start_cmds="$start_cmds $server_cmd" stop "$server_cmd" this_rc=$? [ $this_rc != 0 ] && rc=$this_rc server_cmd= done # Now restart the servers that were running if [ "X$start_cmds" != "X" ]; then echo start "$start_cmds" local this_rc=$? [ $this_rc != 0 ] && rc=$this_rc fi return $rc } #------------------------------------------------------------------ # Mainline script #------------------------------------------------------------------ CMD=$1 shift 1 prepare_log_dir if [ $? -ne 0 ]; then echo $"Failed to make log directory (`dirname $LOG_FILE`)" >&2 exit 1 fi OPTS=`getopt -s bash -u -o 'a:B:c:iI:n:p:r:R:u:' -- $@` if [ $? -ne 0 ]; then echo "Error: Argument parse error: $@" >&2 echo_usage exit 2 fi # Initialize server specs OPT_SERVER_CMDS= parse_args $OPTS || exit 2 load_config RETVAL=0 case $CMD in start) # Start specified servers. If none specified, start configured servers start "$OPT_SERVER_CMDS" RETVAL=$? ;; stop) # Stop specified servers stop "$OPT_SERVER_CMDS" RETVAL=$? ;; # Restart specified servers restart) restart "$OPT_SERVER_CMDS" RETVAL=$? ;; # Restart specified servers if they are running condrestart|try-restart) condrestart "$OPT_SERVER_CMDS" RETVAL=$? ;; # Give status on specified servers status) status "$OPT_SERVER_CMDS" exit $? ;; # Reloading config without stop/restart is not supported reload) RETVAL=3 ;; # Reload config with stop/start force-reload) # stop all running servers stop echo # Restart specified servers # If none specified, restart configured servers start "$OPT_SERVER_CMDS" RETVAL=$? ;; usage|*) echo_usage RETVAL=0 ;; esac echo exit $RETVAL