summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Brolley <brolley@redhat.com>2008-05-30 15:41:22 -0400
committerDave Brolley <brolley@redhat.com>2008-05-30 15:41:22 -0400
commitab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99 (patch)
tree9b02742034edcc488a484b6a1a0a767df2bc5214
parent921d61030221bc5ff27f8d63d3fa30e93f659484 (diff)
downloadsystemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.tar.gz
systemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.tar.xz
systemtap-steved-ab19dac7f34abd7fa4fa3f0c277ee7d3ca3bbe99.zip
New systemptap compile client and server scripts.
-rwxr-xr-xstap-client588
-rwxr-xr-xstap-server436
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