#!/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 port=$1 test "X$port" = "X" && port=65001 } # function: initialization function initialization { # Initialization wd=`pwd` # Default options settings p_phase=5 keep_temps=0 } # function: receive_request # # Receive a tar file representing the request from the client: # The protocol is: # client -> "request:" # server -> "ready:" # client -> $tar_client function receive_request { # Request from the client is on stdin read line=$REPLY # Check to see that it is a client request test "$line" = "request:" || \ fatal "ERROR: client request, '$line' is incorrect" # Create a place to receive the client's tar file tar_client=`mktemp -t $tmpdir_prefix_server.client.tgz.XXXXXX` || \ fatal "ERROR: cannot create temporary tar file " $tar_client # Request that the file be sent. echo "ready:" # Receive the file. nc -l $port < /dev/null > $tar_client } # function: unpack_request # # Unpack the tar file received from the client and make the contents # available for use when running 'stap' function unpack_request { # 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` cd $tmpdir_server # Unpack the tar file. tar -xzf $tar_client || \ fatal "ERROR: cannot unpack tar archive $tar_client" # Identify the client's request tree. The tar file should have expanded # into a single directory named to match $tmpdir_prefix_client.?????? # which should now be the only item in the current directory. test "`ls | wc -l`" = 1 || \ fatal "ERROR: Wrong number of files after expansion of client's tar file" tmpdir_client=`ls` tmpdir_client=`expr "$tmpdir_client" : "\\\($tmpdir_prefix_client\\\\.......\\\)"` test "X$tmpdir_client" != "X" || \ fatal "ERROR: client tar file did not expand as expected" # Move the client's temp directory to a local temp location local local_tmpdir_client=`mktemp -dt $tmpdir_prefix_server.client.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 check_compatibility "$client_sysinfo" "`server_sysinfo`" } # function server_sysinfo # # Generate the server's sysinfo and echo it to stdout function server_sysinfo { if test "X$sysinfo_server" = "X"; then # Add some info from uname sysinfo_server="`uname -rvm`" fi echo $sysinfo_server } # function check_compaibility SYSINFO1 SYSINFO2 # # Make sure that systemtap as described by SYSINFO1 and SYSINFO2 are compaible function check_compatibility { # TODO: This needs work # - Make sure the linux kernel matches exactly local sysinfo1=$1 local sysinfo2=$2 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 "done:" # server -> $tar_server function send_response { echo "done:" # Now send it nc -l $port < $tar_server > /dev/null } # 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