diff options
author | Dave Brolley <brolley@redhat.com> | 2008-05-30 15:41:22 -0400 |
---|---|---|
committer | Dave Brolley <brolley@redhat.com> | 2008-05-30 15:41:22 -0400 |
commit | ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99 (patch) | |
tree | 9b02742034edcc488a484b6a1a0a767df2bc5214 | |
parent | 921d61030221bc5ff27f8d63d3fa30e93f659484 (diff) | |
download | systemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.tar.gz systemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.tar.xz systemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.zip |
New systemptap compile client and server scripts.
-rwxr-xr-x | stap-client | 588 | ||||
-rwxr-xr-x | stap-server | 436 |
2 files changed, 1024 insertions, 0 deletions
diff --git a/stap-client b/stap-client new file mode 100755 index 00000000..53f33e33 --- /dev/null +++ b/stap-client @@ -0,0 +1,588 @@ +#!/bin/bash + +# Compile server client for systemtap +# +# Copyright (C) 2008 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 examines the systemtap command line and packages the files and +# information needed to execute the command. This is then sent to a trusted +# systemtap server which will process the request and return the resulting +# kernel module (if requested) and any other information generated by the +# request. If a kernel module is generated, this script will load the module +# and execute it using 'staprun', if requested. + +#----------------------------------------------------------------------------- +# Helper functions. +#----------------------------------------------------------------------------- +# function: configuration +function configuration { + tmpdir_prefix_client=stap.client + tmpdir_prefix_server=stap.server + port=65000 +} + +# function: initialization +function initialization { + wd=`pwd` + umask 0 + + # Default options settings + p_phase=5 + v_level=0 + keep_temps=0 + + # Create a temporary directory to package things in + # Do this before parsing the command line so that there is a place + # to put -I and -R directories. + tmpdir_client=`mktemp -dt $tmpdir_prefix_client.XXXXXX` || \ + fatal "ERROR: cannot create temporary directory " $tmpdir_client + tmpdir_env=`dirname $tmpdir_client` +} + +# function: parse_options [ STAP-OPTIONS ] +# +# Examine the command line. We need not do much checking, but we do need to +# parse all options in order to discover the ones we're interested in. +# The server will take care of most situations and return the appropriate +# output. +# +function parse_options { + cmdline= + while test $# != 0 + do + advance_p=0 + dash_seen=0 + + # Start of a new token. + first_token=$1 + until test $advance_p != 0 + do + # Identify the next option + first_char=`expr "$first_token" : '\(.\).*'` + if test $dash_seen = 0; then + if test "$first_char" = "-"; then + if test "$first_token" != "-"; then + # It's not a lone dash, so it's an option. Remove the dash. + first_token=`expr "$first_token" : '-\(.*\)'` + dash_seen=1 + first_char=`expr "$first_token" : '\(.\).*'` + cmdline="$cmdline -" + fi + fi + if test $dash_seen = 0; then + # The dash has not been seen. This is either the script file + # name or an arument to be passed to the probe module. + # If this is the first time, and -e has not been specified, + # then it could be the name of the script file. + if test "X$e_script" = "X" -a "X$script_file" = "X"; then + script_file=$first_token + fi + advance_p=$(($advance_p + 1)) + cmdline="$cmdline $first_token" + break + fi + fi + + # We are at the start of an option. Look at the first character. + case $first_char in + c) + get_arg $first_token "$2" + process_c "$stap_arg" + ;; + D) + get_arg $first_token $2 + cmdline="${cmdline}D '$stap_arg'" + ;; + e) + get_arg $first_token "$2" + process_e "$stap_arg" + ;; + I) + get_arg $first_token $2 + process_I $stap_arg + ;; + k) + keep_temps=1 + ;; + l) + get_arg $first_token $2 + cmdline="${cmdline}l '$stap_arg'" + ;; + m) + get_arg $first_token $2 + cmdline="${cmdline}m $stap_arg" + ;; + o) + get_arg $first_token $2 + process_o $stap_arg + ;; + p) + get_arg $first_token $2 + process_p $stap_arg + ;; + r) + get_arg $first_token $2 + cmdline="${cmdline}r $stap_arg" + ;; + R) + get_arg $first_token $2 + process_R $stap_arg + ;; + s) + get_arg $first_token $2 + cmdline="${cmdline}s $stap_arg" + ;; + v) + v_level=$(($v_level + 1)) + ;; + x) + get_arg $first_token $2 + cmdline="${cmdline}x $stap_arg" + ;; + *) + # An unknown or unimportant flag. Ignore it, but pass it on to the server. + ;; + esac + + if test $advance_p = 0; then + # Just another flag character. Consume it. + cmdline="$cmdline$first_char" + first_token=`expr "$first_token" : '.\(.*\)'` + if test "X$first_token" = "X"; then + advance_p=$(($advance_p + 1)) + fi + fi + done + + # Consume the arguments we just processed. + while test $advance_p != 0 + do + shift + advance_p=$(($advance_p - 1)) + done + done + + # If the script file was given and it's not '-', then replace it with its + # client-temp-name in the command string. + if test "X$script_file" != "X" -a "$script_file" != "-"; then + local local_name=`generate_client_temp_name $script_file` + cmdline=`echo $cmdline | sed s,$script_file,script/$local_name,` + fi +} + +# function: get_arg FIRSTWORD SECONDWORD +# +# Collect an argument to the given option +function get_arg { + # Remove first character. + local opt=`expr "$1" : '\(.\).*'` + local first=`expr "$1" : '.\(.*\)'` + + # Advance to the next token, if the first one is exhausted. + if test "X$first" = "X"; then + shift + advance_p=$(($advance_p + 1)) + first=$1 + fi + + test "X$first" != "X" || \ + fatal "Missing argument to -$opt" + + stap_arg="$first" + advance_p=$(($advance_p + 1)) +} + +# function: process_c ARGUMENT +# +# Process the -c flag. +function process_c { + c_cmd="$1" + cmdline="${cmdline}c '$1'" +} + +# function: process_e ARGUMENT +# +# Process the -e flag. +function process_e { + # Only the first -e option is recognized and it overrides any script file name + # which may have already been identified. + if test "X$e_script" = "X"; then + e_script="$1" + script_file= + fi + cmdline="${cmdline}e '$1'" +} + +# function: process_I ARGUMENT +# +# Process the -I flag. +function process_I { + local local_name=`include_file_or_directory tapsets $1` + test "X$local_name" != "X" || return + cmdline="${cmdline}I $local_name" +} + +# function: process_o ARGUMENT +# +# Process the -o flag. +function process_o { + stdout_redirection="> $1" + cmdline="${cmdline}o $1" +} + +# function: process_p ARGUMENT +# +# Process the -p flag. +function process_p { + p_phase=$1 + cmdline="${cmdline}p '$1'" +} + +# function: process_R ARGUMENT +# +# Process the -R flag. +function process_R { + local local_name=`include_file_or_directory runtime $1` + test "X$local_name" != "X" || return + cmdline="${cmdline}R $local_name" +} + +# function: include_file_or_directory PREFIX NAME +# +# Include the given file or directory in the client's temporary +# tree to be sent to the server. +function include_file_or_directory { + # Add a symbolic link of the named directory to our temporary directory + local local_name=`generate_client_temp_name $2` + mkdir -p $tmpdir_client/$1/`dirname $local_name` || \ + fatal "ERROR: could not create $tmpdir_client/$1/`dirname $local_name`" + ln -s /$local_name $tmpdir_client/$1/$local_name || \ + fatal "ERROR: could not link $tmpdir_client/$1/$local_name to /$local_name" + echo $local_name +} + +# function: generate_client_temp_name NAME +# +# Generate the name to be used for the given file/directory relative to the +# client's temporary directory. +function generate_client_temp_name { + # Transform the name into a fully qualified path name + local full_name=`echo $1 | sed "s,^\\\([^/]\\\),$wd/\\\\1,"` + + # The same name without the initial / or trailing / + local local_name=`echo $full_name | sed 's,^/\(.*\),\1,'` + local_name=`echo $local_name | sed 's,\(.*\)/$,\1,'` + echo $local_name +} + +# function: create_request +# +# Add information to the client's temp directory representing the request +# to the server. +function create_request { + # Work in our temporary directory + cd $tmpdir_client + + if test "X$script_file" != "X"; then + if test "$script_file" = "-"; then + mkdir -p $tmpdir_client/script || \ + fatal "ERROR: cannot create temporary diectory " $tmpdir_client/script + cat > $tmpdir_client/script/- + else + include_file_or_directory script $script_file > /dev/null + fi + fi + + # Add the necessary info to special files in our temporary directory. Do this + # after linking in -I and -R directories in order to guarantee no name clashes. + tmpfile=`mktemp cmdline.XXXXXX` || \ + fatal "ERROR: cannot create temporary file " + echo "cmdline: $cmdline" > $tmpfile + + tmpfile=`mktemp sysinfo.XXXXXX` || \ + fatal "ERROR: cannot create temporary file " $tmpfile + echo "sysinfo: `client_sysinfo`" > $tmpfile +} + +# function client_sysinfo +# +# Generate the client's sysinfo and echo it to stdout +function client_sysinfo { + if test "X$sysinfo_client" = "X"; then + # Get the stap version + stap_version=`stap -V 2>&1 | grep version` + # Remove the number before the first slash + stap_version=`expr "$stap_version" : '.*version [^/]*/\([0-9./]*\).*'` + # Add some info from uname + sysinfo_client="stap $stap_version `uname -sr`" + fi + echo $sysinfo_client +} + +# function: package_request +# +# Package the client's temp directory into a form suitable for sending to the +# server. +function package_request { + # Package up the temporary directory into a tar file + cd $tmpdir_env + + local tmpdir_client_base=`basename $tmpdir_client` + tar_client=$tmpdir_env/`mktemp $tmpdir_client_base.tgz.XXXXXX` || \ + fatal "ERROR: cannot create temporary file " $tar_client + + tar -czhf $tar_client $tmpdir_client_base || \ + fatal "ERROR: tar of request tree, $tmpdir_client, failed" + + tar_server=$tmpdir_env/`mktemp $tmpdir_prefix_server.tgz.XXXXXX` || \ + fatal "ERROR: cannot create temporary file " $tar_server +} + +# function: send_request +# +# Notify the server and then send $tar_client to the server as $tar_server +# The protocol is: +# client -> "request: $tmpdir_client" +# server -> "send: $tar_server" +# client: rsync local:$tar_client server:$tar_server +# client -> "waiting:" +# +# $tmpdir_client is provided on the request so that the server knows what +# the tar file will expand to. +function send_request { + echo "request: `basename $tmpdir_client`" >&3 + + read <&3 + local line=$REPLY + check_server_error $line + + local tar_dest=`expr "$line" : 'send: \([^ ]*\)$'` + test "X$tar_dest" == "X" && \ + fatal "ERROR: destination tar file not provided" + + rsync -essh -a --delete $tar_client root@$server:$tar_dest || \ + fatal "ERROR: rsync of client request, $tar_client to $server:$tar_dest, failed" + + echo "waiting:" >&3 +} + +# function: receive_response +# +# Wait for a response from the server indicating the results of our request. +# The protocol is: +# server -> "sending: remote-tar-name server-tempdir-name stap-tempdir-name" +# client -> "OK" +function receive_response { + read <&3 + local line=$REPLY + check_server_error $line + + tar_dest=`expr "$line" : 'sending: \([^ ]*\) [^ ]* [^ ]*$'` + test "X$tar_dest" == "X" && \ + fatal "ERROR: server response remote file is missing" + + tmpdir_server=`expr "$line" : 'sending: [^ ]* \([^ ]*\) [^ ]*$'` + test "X$tmpdir_server" == "X" && \ + fatal "ERROR: server response temp dir is missing" + + tmpdir_stap=`expr "$line" : 'sending: [^ ]* [^ ]* \([^ ]*\)$'` + test "X$tmpdir_stap" == "X" && \ + fatal "ERROR: server response stap temp dir is missing" + + + # Retrieve the file + rsync -essh -a --delete root@$server:$tar_dest $tar_server || \ + fatal "ERROR: rsync of server response, $server:$tar_dest to $tar_server, failed" + echo "OK" >&3 +} + +# function: unpack_response +# +# Unpack the tar file received from the server and make the contents available +# for printing the results and/or running 'staprun'. +function unpack_response { + # Unpack the server output directory + cd $tmpdir_client + tar -xzf $tar_server || \ + fatal "ERROR: Unpacking of server response, $tar_server, failed" + + # Create a local location for the server response. + local local_tmpdir_server_base=`basename $tar_server | sed 's,\.tgz,,'` + local local_tmpdir_server=`mktemp -dt $local_tmpdir_server_base.XXXXXX` || \ + fatal "ERROR: cannot create temporary directory " $local_tmpdir_server + + # Move the systemtap temp directory to our a local temp location, if -k + # was specified. + if test $keep_temps = 1; then + local local_tmpdir_stap=`mktemp -dt stapXXXXXX` || \ + fatal "ERROR: cannot create temporary directory " $local_tmpdir_stap + mv $tmpdir_server/$tmpdir_stap/* $local_tmpdir_stap 2>/dev/null + rm -fr $tmpdir_server/$tmpdir_stap + + # Correct the name of the temp directory in the server's stderr output + sed -i "s,^Keeping temporary directory.*,Keeping temporary directory \"$local_tmpdir_stap\"," $tmpdir_server/stderr + tmpdir_stap=$local_tmpdir_stap + else + tmpdir_stap=$local_tmpdir_server/$tmpdir_stap + fi + + # Move the extracted tree to our local location + mv $tmpdir_server/* $local_tmpdir_server + rm -fr $tmpdir_server + tmpdir_server=$local_tmpdir_server +} + +# function: find_and_connect_to_server +# +# Find and establish connection with a compatibale stap server. +function find_and_connect_to_server { + # Find a server + server=`avahi-browse --all --terminate | match_server` + test "X$server" != "X" || \ + fatal "ERROR: cannot find a server" + + # Open a connection to the server + if ! exec 3<> /dev/tcp/$server/$port; then + fatal "ERROR: cannot connect to server at /dev/tcp/$server/$port" + fi +} + +# function: match_server +# +# Find a suitable server using the avahi-browse output provided on stdin. +function match_server { + local server_ip + + # Loop over the avahi service descriptors. + while read + do + # Examine the next service descriptor + avahi_service=$REPLY + + # Is it a stap server? + server_name=`expr "$avahi_service" : '\+.*stap.*on \([^ ]*\).*'` + if test "X$server_name" = "X"; then + continue + fi + + # It is a stap server, but is it compatible? + sysinfo_server=`expr "$avahi_service" : '\+.*stap[^/]*/\(.*\) on.*'` + sysinfo_server="stap $sysinfo_server" + if test "$sysinfo_server" != "`client_sysinfo`"; then + continue + fi + + # Now resolve the server's ip address. First find the domain. + domain_name=`expr "$avahi_service" : '\+.* \([^ ]*\)$'` + + # Sometimes (seems random), avahi-resolve-host-name resolves a local server to its + # hardware address rather its ip address. Keep trying until we get + # an ip address. + local attempt + for ((attempt=0; $attempt < 5; ++attempt)) + do + # Resolve the server.domain to an ip address. + server=`avahi-resolve-host-name $server_name.$domain_name` + server=`expr "$server" : '.* \(.*\)$'` + + server_ip=`expr "$server" : '^\([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\)$'` + if test "X$server_ip" != "X"; then + break + fi + done + if test "X$server_ip" != "X"; then + break + fi + done + + echo $server_ip +} + +# function: disconnect_from_server +# +# Disconnect from the server. +function disconnect_from_server { + # Close the connection to the server. + exec 3<&- +} + +# function: stream_output +# +# Write the stdout and stderr from the server to stdout and stderr respectively. +function stream_output { + # Output stdout and stderr as directed + cd $local_tmpdir_server + cat ${tmpdir_server}/stderr >&2 + eval cat ${tmpdir_server}/stdout $stdout_redirection +} + +# function: maybe_call_staprun +# +# Call staprun using the module returned from the server, if requested. +function maybe_call_staprun { + if test $p_phase = 5; then + for ((--v_level; $v_level > 0; --v_level)) + do + staprun_opts="$staprun_opts -v" + done + staprun $staprun_opts \ + $tmpdir_stap/`ls $tmpdir_stap | grep '.ko$'` + fi +} + +# function: check_server_error SERVER_RESPONSE +# +# Check the given server response for an error message. +function check_server_error { + echo $1 | grep -q "^ERROR:" && \ + fatal "Server:" "$@" +} + +# function: fatal [ MESSAGE ] +# +# Fatal error +# Prints its arguments to stderr and exits +function fatal { + echo $0: "$@" >&2 + cat <&3 >&2 + cleanup + exit 1 +} + +# function cleanup +# +# Cleanup work files unless asked to keep them. +function cleanup { + # Clean up. + cd $tmpdir_env + if test $keep_temps != 1; then + rm -fr $tmpdir_client + rm -f $tar_client + rm -f $tar_server + rm -fr $tmpdir_server + fi +} + +#----------------------------------------------------------------------------- +# Beginning of main line execution. +#----------------------------------------------------------------------------- +configuration +initialization +parse_options "$@" +create_request +package_request +find_and_connect_to_server +send_request +receive_response +unpack_response +disconnect_from_server +stream_output +maybe_call_staprun +cleanup + +exit 0 diff --git a/stap-server b/stap-server new file mode 100755 index 00000000..1a2e7918 --- /dev/null +++ b/stap-server @@ -0,0 +1,436 @@ +#!/bin/bash + +# Compile server for systemtap +# +# Copyright (C) 2008 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 unpacks the tar file provided on stdin and uses the information +# contained in the unpacked tree to build the requested systemtap kernel module. +# This module is then written to stdout. + +#----------------------------------------------------------------------------- +# Helper functions. +#----------------------------------------------------------------------------- +# function: configuration +function configuration { + # Configuration + tmpdir_prefix_client=stap.client + tmpdir_prefix_server=stap.server +} + +# function: initialization +function initialization { + # Initialization + wd=`pwd` + + # Default options settings + p_phase=5 + keep_temps=0 + + # Make a temp directory to work in. + tmpdir_server=`mktemp -dt $tmpdir_prefix_server.XXXXXX` || \ + fatal "ERROR: cannot create temporary directory " $tmpdir_server + tmpdir_env=`dirname $tmpdir_server` +} + +# function: receive_request +# +# Receive a tar file representing the request from the client: +# The protocol is: +# client -> "request: $tmpdir_client" +# server -> "send: $tar_client" +# client: copies file to server:$tar_client +# client -> "waiting:" +# +# $tmpdir_client is provided on the request so that we know what +# the tar file will expand to. +function receive_request { + cd $tmpdir_server + + # Request from the client is on stdin + read + line=$REPLY + + # Extract the name of the client's temp directory. + tmpdir_client=`expr "$line" : 'request: \([^ ]*\)$'` + test "X$tmpdir_client" == "X" && \ + fatal "ERROR: client request temp dir name is missing" $tmpdir_server + tmpdir_client=$tmpdir_server/$tmpdir_client + + # Create the client's temp dir now to guarantee that it won't clash with + # any files we need to create later. + mkdir $tmpdir_client || \ + fatal "ERROR: cannot create client temp dir" $tmpdir_client + + # Create a place to receive the client's tar file + tar_client=`mktemp -t $tmpdir_prefix_client.tgz.XXXXXX` || \ + fatal "ERROR: cannot create temporary tar file " $tar_client + + # Request that the file be sent. + echo "send: $tar_client" + + # Wait for confirmation that the tar file has arrived via rysnc + read + line=$REPLY + test "X$line" = "Xwaiting:" || \ + fatal "ERROR: client send confirmation, '$line', is incorrect" +} + +# function: unpack_request +# +# Unpack the tar file received from the client and make the contents +# available for use when running 'stap' +function unpack_request { + cd $tmpdir_server + + # Unpack the tar file. + tar -xzf $tar_client || \ + fatal "ERROR: cannot unpack tar archive $tar_client" + + # Move the client's temp directory to a local temp location + local tmpdir_client_base=`basename $tar_client | sed 's,\.tgz,,'` + local local_tmpdir_client=`mktemp -dt $tmpdir_client_base.XXXXXX` || \ + fatal "ERROR: cannot create temporary tar file " $local_tmpdir_client + mv $tmpdir_client/* $local_tmpdir_client + rm -fr $tmpdir_client + tmpdir_client=$local_tmpdir_client +} + +# function: check_request +# +# Examine the contents of the request to make sure that they are valid. +function check_request { + # Work in the temporary directory provided by the client + cd $tmpdir_client + + # Add the necessary info from files in our temporary directory. + cmdline=`read_data_file cmdline` + test "X$cmdline" != "X" || exit 1 + client_sysinfo=`read_data_file sysinfo` + test "X$client_sysinfo" != "X" || exit 1 + + # Extract the client's config info. + client_name=`expr "$client_sysinfo" : 'stap [^ ]* [^ ]* \([^ ]*\).*'` + client_sysinfo=`echo $client_sysinfo | sed s,$client_name,,` + + # Extract the server's config info. + server_sysinfo=`uname -sr` + server_name=`expr "$server_sysinfo" : '[^ ]* \([^ ]*\).*'` + server_sysinfo=`echo $server_sysinfo | sed s,$server_name,,` + local stap_version=`stap -V 2>&1 | grep version` + stap_version=`expr "$stap_version" : '.*version \([0-9./]*\).*'` + server_sysinfo="stap $stap_version $server_sysinfo" + + check_compatibility "$client_sysinfo" "$server_sysinfo" +} + +# function check_compaibility SYSINFO1 SYSINFO2 +# +# Make sure that systemtap as described by SYSINFO1 and SYSINFO2 are compaible +function check_compatibility { + # TODO: This needs work + # - In stap version x/y, require that the y matches + # - Make sure the linux kernel matches exactly + local sysinfo1=`echo $1 | sed 's,stap [^/]*/,stap ,'` + local sysinfo2=`echo $2 | sed 's,stap [^/]*/,stap ,'` + + if test "$sysinfo1" != "$sysinfo2"; then + error "ERROR: system configuration mismatch" + error " client: $sysinfo1" + fatal " server: $sysinfo2" + fi +} + +# function: read_data_file PREFIX +# +# Find a file whose name matches '$1.??????' whose first line +# contents are '$1: .*'. Read and echo the first line. +function read_data_file { + for f in `ls $1.??????` + do + read < $f + line=$REPLY + data=`expr "$line" : "$1: \\\(.*\\\)"` + if test "X$data" != "X"; then + echo $data + return + fi + done + fatal "ERROR: Data file for $1 not found" +} + +# function: parse_options [ STAP-OPTIONS ] +# +# Examine the command line. We need not do much checking, but we do need to +# parse all options in order to discover the ones we're interested in. +function parse_options { + while test $# != 0 + do + advance_p=0 + dash_seen=0 + + # Start of a new token. + first_token=$1 + until test $advance_p != 0 + do + # Identify the next option + first_char=`expr "$first_token" : '\(.\).*'` + if test $dash_seen = 0; then + if test "$first_char" = "-"; then + if test "$first_token" != "-"; then + # It's not a lone dash, so it's an option. Remove the dash. + first_token=`expr "$first_token" : '-\(.*\)'` + dash_seen=1 + first_char=`expr "$first_token" : '\(.\).*'` + fi + fi + if test $dash_seen = 0; then + # The dash has not been seen. This is either the script file + # name or an arument to be passed to the probe module. + # If this is the first time, and -e has not been specified, + # then it could be the name of the script file. + if test "X$e_script" = "X" -a "X$script_file" = "X"; then + script_file=$first_token + fi + advance_p=$(($advance_p + 1)) + break + fi + fi + + # We are at the start of an option. Look at the first character. + case $first_char in + c) + get_arg $first_token "$2" + ;; + D) + get_arg $first_token $2 + ;; + e) + get_arg $first_token "$2" + process_e "$stap_arg" + ;; + I) + get_arg $first_token $2 + ;; + k) + keep_temps=1 + ;; + l) + get_arg $first_token $2 + ;; + m) + get_arg $first_token $2 + ;; + o) + get_arg $first_token $2 + ;; + p) + get_arg $first_token $2 + process_p $stap_arg + ;; + r) + get_arg $first_token $2 + ;; + R) + get_arg $first_token $2 + ;; + s) + get_arg $first_token $2 + ;; + x) + get_arg $first_token $2 + ;; + *) + # An unknown flag. Ignore it. + ;; + esac + + if test $advance_p = 0; then + # Just another flag character. Consume it. + first_token=`expr "$first_token" : '.\(.*\)'` + if test "X$first_token" = "X"; then + advance_p=$(($advance_p + 1)) + fi + fi + done + + # Consume the arguments we just processed. + while test $advance_p != 0 + do + shift + advance_p=$(($advance_p - 1)) + done + done +} + +# function: get_arg FIRSTWORD SECONDWORD +# +# Collect an argument to the given option +function get_arg { + # Remove first character. Advance to the next token, if the first one + # is exhausted. + local first=`expr "$1" : '.\(.*\)'` + if test "X$first" = "X"; then + shift + advance_p=$(($advance_p + 1)) + first=$1 + fi + + stap_arg="$first" + advance_p=$(($advance_p + 1)) +} + +# function: process_e ARGUMENT +# +# Process the -e flag. +function process_e { + if test "X$e_script" = "X"; then + e_script="$1" + script_file= + fi +} + +# function: process_p ARGUMENT +# +# Process the -p flag. +function process_p { + if test $1 -ge 1 -a $1 -le 5; then + p_phase=$1 + fi +} + +# function: call_stap +# +# Call 'stap' with the options provided. Don't run past phase 4. +function call_stap { + # Invoke systemtap. + # Use -k so we can return results to the client + # Limit to -p4. i.e. don't run the module + cd $tmpdir_client + if test $p_phase -gt 4; then + server_p_phase=4 + else + server_p_phase=$p_phase + fi + eval stap $cmdline -k -p $server_p_phase \ + >> $tmpdir_server/stdout \ + 2>> $tmpdir_server/stderr +} + +# function: create_response +# +# Add information to the server's temp directory representing the response +# to the client. +function create_response { + cd $tmpdir_server + + # Get the name of the stap temp directory, which was kept, from stderr. + tmpdir_line=`cat stderr | grep "Keeping temp"` + tmpdir_stap=`expr "$tmpdir_line" : '.*"\(.*\)".*'` + + # Remove the message about keeping th<e stap temp directory from stderr, unless + # the user did request to keep it. + if test $keep_temps != 1; then + sed -i "/^Keeping temp/d" stderr + fi + + # If the user specified -p5, remove the name of the kernel module from stdout. + if test $p_phase = 5; then + sed -i '/\.ko$/d' stdout + fi + + # Add the contents of the stap temp directory to the server output directory + ln -s $tmpdir_stap `basename $tmpdir_stap` +} + +# function: package_response +# +# Package the server's temp directory into a form suitable for sending to the +# client. +function package_response { + cd $tmpdir_env + # Create a place to generate our tar file of our temporary directory + local tmpdir_server_base=`basename $tmpdir_server` + tar_server=$tmpdir_env/`mktemp $tmpdir_server_base.tgz.XXXXXX` || \ + fatal "ERROR: cannot create temporary tar file " $tar_server + chmod +r $tar_server + + # Generate the tar file + tar -czphf $tar_server `basename $tmpdir_server` || \ + fatal "ERROR: tar of $tmpdir_server failed" +} + +# function: send_response +# +# Notify the client that $tar_server is ready and wait for the client to take +# it. +# The protocol is: +# server -> "sending: $tar_server $tmpdir_server $tmpdir_stap" +# client: copies file from server:$tar_server +# client -> "OK" +function send_response { + # TODO needed for rsync from client to work + chmod +r $tmpdir_server + + # Tell the client where to get it. + echo "sending: $tar_server `basename $tmpdir_server` `basename $tmpdir_stap`" + + # Wait for the confirmation + read + line=$REPLY + test $line = "OK" || \ + fatal "ERROR: client final confirmation, '$line' is incorrect" +} + +# function: fatal [ MESSAGE ] +# +# Fatal error +# Prints its arguments to stderr and exits +function fatal { + echo "$@" >&2 + cleanup + exit 1 +} + +# Non fatal error +# Prints its arguments to stderr but does not exit +function error { + echo "$@" >&2 +} + +# function cleanup +# +# Cleanup work files unless asked to keep them. +function cleanup { + # Clean up. + cd $tmpdir_env + if test $keep_temps != 1; then + rm -fr $tar_client + rm -fr $tmpdir_client + rm -fr $tar_server + rm -fr $tmpdir_server + rm -fr $tmpdir_stap + fi +} + +#----------------------------------------------------------------------------- +# Beginning of main line execution. +#----------------------------------------------------------------------------- +configuration +initialization +receive_request +unpack_request +check_request +eval parse_options $cmdline +call_stap +create_response +package_response +send_response +cleanup + +exit 0 |