diff options
author | Masami Hiramatsu <mhiramat@redhat.com> | 2009-01-30 18:54:06 -0500 |
---|---|---|
committer | Masami Hiramatsu <mhiramat@redhat.com> | 2009-01-30 18:54:06 -0500 |
commit | 9a8d8be369cd1d2ac148b367e1c4b74ab9a005ba (patch) | |
tree | eee895a1ac6b4a94f268647b031708fc17e102dc /initscript/systemtap.in | |
parent | 255b5e73f14c9fc72e5566edf595ab8df184f14f (diff) | |
download | systemtap-steved-9a8d8be369cd1d2ac148b367e1c4b74ab9a005ba.tar.gz systemtap-steved-9a8d8be369cd1d2ac148b367e1c4b74ab9a005ba.tar.xz systemtap-steved-9a8d8be369cd1d2ac148b367e1c4b74ab9a005ba.zip |
PR6936: Add systemtap initscript and systemtap-initscript subpackage.
Diffstat (limited to 'initscript/systemtap.in')
-rw-r--r-- | initscript/systemtap.in | 664 |
1 files changed, 664 insertions, 0 deletions
diff --git a/initscript/systemtap.in b/initscript/systemtap.in new file mode 100644 index 00000000..eaa1d969 --- /dev/null +++ b/initscript/systemtap.in @@ -0,0 +1,664 @@ +#!/bin/bash +# +# systemtap Startup script for systemtap scrips +# +# chkconfig: - 00 99 +# description: Systemtap is a programable kernel/application tracing tool. +# config: /etc/systemtap/config +# config: /etc/systemtap/conf.d +### BEGIN INIT INFO +# Provides: Systemtap scripts startup +# Required-Start: $local_fs +# Required-Stop: $local_fs +# Short-Description: start and stop systemtap scripts +# Description: Systemtap is a programable kernel/application tracing tool. +### END INIT INFO + +# Source function library. +. /etc/rc.d/init.d/functions + +prog=systemtap + +# Commands +STAP=@bindir@/stap +STAPRUN=@bindir@/staprun +UNAME=/bin/uname +LSMOD=/sbin/lsmod + +# Path setup +SCRIPT_PATH=/etc/systemtap/script.d +CONFIG_PATH=/etc/systemtap/conf.d +CACHE_PATH=/var/cache/systemtap +STAT_PATH=/var/run/systemtap +TEMP_PATH=/tmp +LOG_FILE=/var/log/systemtap.log + +# FAIL unless all scripts succeeded to run +PASSALL=yes + +# Always follows script dependencies +RECURSIVE=no + +# Automatically recompile scripts if caches are old or do not exist. +AUTOCOMPILE=yes + +# Start these scripts by default. If omitted, all scripts are started. +DEFAULT_START= + +# Allow cache only scripts +ALLOW_CACHEONLY=no + +# Optional settings +CONFIG=/etc/systemtap/config +SCRIPTS= +KRELEASE=`uname -r` +OPT_RECURSIVE= +OPT_SCRIPTS= +OPTS= +OPT_ASSUMEYES= + +echo_usage () { + echo $"Usage: $prog {start|stop|restart|status|compile|cleanup} [option]" + echo $"Options:" + echo $" -c configfile : specify config file" + echo $" -r kernelrelease: specify kernel release version" + echo $" -R : recursively dependency checking" + echo $" -y : answer yes for all questions." + echo $" script(s) : specify systemtap scripts" +} + +#----------------------------------------------------------------- +# 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_warning () { # message + slog "Warning: $1" + warning "$1" +} +do_failure () { # message + slog "Error: $1" + failure "$1" +} +do_success () { # message + log "Pass: $1" + success "$1" +} +# Normalize options +check_bool () { # value + case $1 in + n|N|no|No|NO|0) + return 0;; + y|Y|yes|Yes|YES|1) + return 1;; + *) + return 2;; + esac +} +ask_yesno () { # message + local yn ret=2 + [ "$OPT_ASSUMEYES" ] && return 1 + while [ $ret -eq 2 ]; do + echo -n "$1 [y/N]: " + read yn + [ -z "$yn" ] && return 0 + check_bool $yn + ret=$? + done + return $ret +} + +#------------------------------------------------------------------ +# Parameter parsing and setup options +#------------------------------------------------------------------ +parse_args () { # arguments + while [ -n "$1" ]; do + case "$1" in + -c) + CONFIG=$2 + shift 1 + ;; + -r) + KRELEASE=$2 + shift 1 + ;; + -R) + OPT_RECURSIVE=1 + ;; + -y) + OPT_ASSUMEYES=1 + ;; + --) + ;; + *) + OPT_SCRIPTS=$OPT_SCRIPTS\ $1 + ;; + esac + shift 1 + done +} + +CMD=$1 +shift 1 +OPTS=`getopt -s bash -u -o 'r:c:R' -- $@` +if [ $? -ne 0 ]; then + slog "Error: Argument parse error: $@" + failure $"parse error" + echo_usage + exit 3 +fi +parse_args $OPTS + +# Include configs +. "$CONFIG" + +for f in "$CONFIG_PATH"/*.conf; do + if [ -f "$f" ]; then + . "$f" + fi +done + +check_bool $PASSALL +PASSALL=$? +check_bool $RECURSIVE +RECURSIVE=$? +if [ "$OPT_RECURSIVE" ]; then # -r option overrides RECURSIVE. + RECURSIVE=1 +fi +check_bool $AUTOCOMPILE +AUTOCOMPILE=$? +CACHE_PATH="$CACHE_PATH/$KRELEASE" + +check_bool $ALLOW_CACHEONLY +ALLOW_CACHEONLY=$? + +__get_all_scripts () { + local s + if [ $ALLOW_CACHEONLY -eq 1 ]; then + for s in "$CACHE_PATH"/*.ko; do + if [ -f "$s" ]; then + basename "$s" | sed s/\.ko$//g + fi + done + fi + for s in "$SCRIPT_PATH"/*.stp; do + if [ -f "$s" ]; then + basename "$s" | sed s/\.stp$//g + fi + done +} + +get_all_scripts() { + __get_all_scripts | sort | uniq +} + +if [ -z "$OPT_SCRIPTS" ]; then + SCRIPTS=`get_all_scripts | xargs` + RECURSIVE=1 +else + SCRIPTS="$OPT_SCRIPTS" +fi + +#------------------------------------------------------------------ +# Main routine +#------------------------------------------------------------------ +NR_FAILS=0 +might_fail () { # message exitcode + if [ $PASSALL -eq 1 ]; then + do_failure "$1" + echo + [ -z "$2" ] && exit 1 + exit $2 + else + log "Warning: "$1 + NR_FAILS=$((NR_FAILS+1)) + return 0 + fi +} +might_success () { # message + if [ $NR_FAILS -ne 0 ]; then + log "Warning: $NR_FAILS failure occured." + do_warning "$1" + else + do_success "$1" + fi + return 0 +} + +get_all_runnings () { + local f + for f in "$STAT_PATH"/*; do + if [ -f "$f" ]; then + basename "$f" + fi + done +} + +get_daemon_pid () { # script + cat "$STAT_PATH/$1" +} + +check_running () { # script + local m f + f="$STAT_PATH/$1" + m=`$LSMOD | grep "^$1 "` + if [ "$m" ]; then + [ -f "$f" ] && return 0 # running + return 4 # another script is running + else + [ -f "$f" ] && return 1 # dead, but pidfile remains + return 3 # dead + fi +} + +# check whether a script cache need to be updated. +check_cache () { # script opts + local s tmp tmp2 + s=$1; shift 1 + [ ! -f "$CACHE_PATH/$s.ko" -o ! -f "$CACHE_PATH/$s.opts" ] && return 1 + if [ $ALLOW_CACHEONLY -ne 1 -o -f "$SCRIPT_PATH/$s.stp" ]; then + [ "$SCRIPT_PATH/$s.stp" -nt "$CACHE_PATH/$s.ko" ] && return 2 + fi + tmp=`head -n 1 "$CACHE_PATH/$s.opts"` + tmp2=`$UNAME -a` + [ "$tmp" != "$tmp2" ] && return 3 + tmp=`tail -n 1 "$CACHE_PATH/$s.opts"` + tmp2="$*" + [ "$tmp" != "$tmp2" ] && return 4 + return 0 +} + +stap_getopt () { # opts + local ret + # TODO: support quoted options + getopt -s bash -u \ + -l 'kelf,kmap::,ignore-vmlinux,ignore-dwarf,vp:' \ + -o 'hVMvtp:I:e:o:R:r:m:kgPc:x:D:bs:uqwl:d:L:F' -- $@ + ret=$? + [ $ret -ne 0 ] && slog "Failed to parse parameters. ($@)" + return $ret +} + +get_compile_opts () { # opts + local opts o skip + opts=`stap_getopt $*` + [ $? -ne 0 ] && return 1 + skip=0 + for o in $opts; do + if [ $skip -ne 0 ]; then skip=0; continue; fi + case $o in + -p|-m|-r|-c|-x|-e|-s|-o) + skip=1 ;; + -h|-V|-k) + ;; + *) + echo -n $o" " ;; + esac + done +} + +get_run_opts () { # normalized_opts + local opts o show + opts=`stap_getopt $*` + [ $? -ne 0 ] && return 1 + show=0 + for o in $opts; do + case $o in + -c|-x|-s|-o) + [ $o == '-s' ] && o='-b' + echo -n $o" " + show=1 + ;; + *) + if [ $show -ne 0 ]; then + echo -n $o" " + show=0 + fi + ;; + esac + done +} + +prepare_cache_dir () { + if [ ! -d "$CACHE_PATH" ]; then + logex mkdir -p "$CACHE_PATH" + [ $? -ne 0 ] && return 1 + fi + return 0 +} + +prepare_stat_dir () { + if [ ! -d "$STAT_PATH" ]; then + logex mkdir -p "$STAT_PATH" + [ $? -ne 0 ] && return 1 + fi + return 0 +} + +compile_script () { # script checkcache + local opts f tmpdir ret + eval f="$SCRIPT_PATH/$1.stp" + if [ ! -f "$f" ]; then + if [ $ALLOW_CACHEONLY -eq 1 ]; then + clog "Warning: no script file($f). Use compiled cache." + return 0 + else + clog "Error: no script file($f)." + return 1 + fi + fi + + eval opts=\$$1_OPT + opts=`get_compile_opts $opts` + [ $? -ne 0 ] && return 2 + + if [ "$2" = "check" ]; then + check_cache $1 $opts + [ $? -eq 0 ] && return 0 # already compiled + if [ $AUTOCOMPILE -eq 0 ]; then + slog "No valid cache for $1" + return 1 + fi + fi + + clog " Compiling $1 ... " -n + tmpdir=`mktemp -d -p "$TEMP_PATH" cache.XXXXXXXX` + if [ $? -ne 0 ]; then + clog "failed to create temporary directory." + return 1 + fi + pushd "$tmpdir" &> /dev/null + logex $STAP -m "$1" -p4 -r $KRELEASE $opts "$f" + ret=$? + if [ $ret -eq 0 ]; then + $UNAME -a > "$1.opts" + echo $opts >> "$1.opts" + logex mv "$1.ko" "$1.opts" "$CACHE_PATH/" + ret=$? + else + slog "Failed to compile script($1)." + fi + popd &> /dev/null + rm -rf $tmpdir + [ $ret -eq 0 ] && clog "done" || clog "error" + return $ret +} + +# dependency resolver +__SORTED= +__follow_dependency () { # script requesters + local opts r reqs s ret + s=$1 + shift 1 + r=`echo \ $*\ | grep \ $s\ ` + if [ -n "$r" ]; then + might_fail $"Dependency loop detected on $s" + return 1 + fi + r=`echo \ $__SORTED\ | grep \ $s\ ` + [ -n "$r" ] && return 0 # already listed up + eval reqs=\$${s}_REQ + if [ -n "$reqs" ]; then + for r in $reqs; do + __follow_dependency $r $s $* + ret=$? + if [ $ret -ne 0 ]; then + return $ret # if one of requires failed, we can't run this script. + fi + done + fi + echo -n "$s " + return 0 +} + +sort_dependency () { # scripts + local s r=0 + __SORTED= + for s in $*; do + __SORTED="$__SORTED "`__follow_dependency $s` + [ $? -ne 0 ] && return 1 + done + echo $__SORTED + return 0 +} + +start_script () { # script + local tmpdir s=$1 + check_running $s + ret=$? + [ $ret -eq 0 ] && return 0 # already running + if [ $ret -eq 4 ]; then + clog "$s is dead, but another script is running." + return 4 + fi + + compile_script $s check + ret=$? + [ $ret -ne 0 ] && return $ret + + eval opts=\$${s}_OPT + opts=`get_run_opts $opts` + [ $? -ne 0 ] && return 2 + + clog " Starting $1 ... " -n + tmpdir=`mktemp -d -p "$TEMP_PATH" cache.XXXXXXXX` # bz7097 + if [ $? -ne 0 ]; then + clog "failed to create temporary directory." + return 1 + fi + pushd "$tmpdir" &> /dev/null + logex $STAPRUN -L $opts "$CACHE_PATH/$s.ko" + ret=$? + if [ $ret -eq 0 ]; then + # TODO: store daemon pid after supporting on-file flight recorder + echo 0 > "$STAT_PATH/$s" + fi + popd &> /dev/null + rm -rf "$tmpdir" + [ $ret -eq 0 ] && clog "done" || clog "error" + return $ret +} + +start () { + local scripts s ret + clog $"Starting $prog: " -n + + if [ -n "$DEFAULT_START" -a -z "$OPT_SCRIPTS" ]; then + SCRIPTS="$DEFAULT_START" + fi + + if [ -z "$SCRIPTS" ]; then + do_warning $"no scripts exist." + return 5 # program is not installed + fi + + prepare_stat_dir + if [ $? -ne 0 ]; then + do_failure $"Failed to make stat directory ($STAT_PATH)" + return 1 + fi + prepare_cache_dir + if [ $? -ne 0 ]; then + do_failure $"Failed to make cache directory ($CACHE_PATH)" + return 1 + fi + + if [ $RECURSIVE -eq 1 ]; then + SCRIPTS=`sort_dependency $SCRIPTS` + if [ $? -ne 0 ]; then + do_failure $"Failed to sort dependency" + return 6 # program is not configured + fi + fi + for s in $SCRIPTS; do + start_script "$s" + ret=$? + if [ $ret -ne 0 ]; then + might_fail $"Failed to run \"$s\". ($ret)" + fi + done + might_success $"$prog startup" + return 0 +} + +stop_script () { # script + local p + check_running "$1" + ret=$? + [ $ret -eq 1 ] && rm -f "$STAT_PATH/$1" + [ $ret -ne 0 ] && return 0 + + p=`get_daemon_pid $1` + if [ $p -ne 0 ]; then + logex killall -TERM $p + else + logex $STAPRUN -d "$1" + fi + [ $? -ne 0 ] && return 1 + rm -f "$STAT_PATH/$1" + return $? +} + +stop () { + local s sl= + clog $"Stopping $prog: " -n + [ -z "$OPT_SCRIPTS" ] && SCRIPTS=`get_all_runnings` + if [ $RECURSIVE -eq 1 ]; then + SCRIPTS=`sort_dependency $SCRIPTS` + if [ $? -ne 0 ]; then + do_failure $"Failed to sort dependency" + return 6 # program is not configured + fi + fi + for s in $SCRIPTS; do + sl="$s $sl" + done + for s in $sl; do + stop_script $s + [ $? -ne 0 ] && might_fail $"Failed to run \"$s\". " + done + might_success $"$prog stopping " + return 0 +} + +status () { + local s pid ret r + [ -z "$SCRIPTS" ] && SCRIPTS=`get_all_runnings` + ret=3 + for s in $SCRIPTS; do + check_running $s + r=$? + [ $ret -ne 0 ] && ret=$r + case $r in + 0) + pid=`get_daemon_pid $s` + [ $pid -ne 0 ] && pid="($pid)" || pid= + echo $"$s$pid is running..." ;; + 1|3) echo $"$s is stopped" ;; + 4) echo $"$s is dead, but another script is running.";; + esac + done + return $ret +} + +compile () { + local s ss + clog $"Compiling $prog: " -n + prepare_cache_dir + if [ $? -ne 0 ]; then + do_failure $"Failed to make cache directory ($CACHE_PATH)" + return 1 + fi + for s in $SCRIPTS; do + ss="$ss "`ls "$CACHE_PATH/$s.ko" "$CACHE_PATH/$s.opts" 2> /dev/null` + done + ss=`echo -n $ss` + if [ "$ss" ]; then + clog "Updating caches: $ss" + ask_yesno "Do you really want to update above caches" + [ $? -eq 0 ] && return 0 + fi + for s in $SCRIPTS; do + compile_script $s nocheck + [ $? -ne 0 ] && might_fail $"$s compilation failed " + done + might_success $"$prog compiled " + return 0 +} + +# Cleanup caches +cleanup () { + local s ss ret + clog $"Cleaning up $prog: " -n + if [ ! -d "$CACHE_PATH" ]; then + do_success "no cache" + return 0 + fi + + for s in $SCRIPTS; do + ss="$ss "`ls "$CACHE_PATH/$s.ko" "$CACHE_PATH/$s.opts" 2> /dev/null` + done + ss=`echo -n $ss` + if [ "$ss" ]; then + echo "Removing caches: $ss" + ask_yesno "Do you really want to remove above caches" + [ $? -eq 0 ] && return 0 + for s in $SCRIPTS; do + logex rm -f "$CACHE_PATH/$s.ko" "$CACHE_PATH/$s.opts" + [ $? -ne 0 ] && might_fail $"failed to clean cache $s.ko" + done + might_success "done" + return 0 + fi +} + +RETVAL=0 + +case $CMD in + start) + start + RETVAL=$? + ;; + stop) + stop + RETVAL=$? + ;; + restart|force-reload) + stop + start + RETVAL=$? + ;; + status) + status + RETVAL=$? + ;; + compile) + compile + RETVAL=$? + ;; + cleanup) + cleanup + RETVAL=$? + ;; + *) + echo_usage + RETVAL=3 + ;; +esac + +echo +exit $RETVAL |