#!/bin/bash # Compile server client for systemtap # # Copyright (C) 2008-2010 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. # Catch ctrl-c and other termination signals trap 'terminate' SIGTERM trap 'interrupt' SIGINT trap 'ignore_signal' SIGHUP SIGPIPE # Initialize the environment . ${PKGLIBEXECDIR}stap-env #----------------------------------------------------------------------------- # Helper functions. #----------------------------------------------------------------------------- # function: initialization function initialization { our_host_name=`expr "$HOSTNAME" : "\\\([a-zA-Z0-9-]*\\\).*"` our_domain_name=`expr "$HOSTNAME" : "$our_host_name\\\(.*\\\)"` rc=0 wd=`pwd` umask 0 # Default location for server certificates if we're not root # Must be owned by us. local uid if test $EUID != 0; then if test -e $stap_user_ssl_db/client; then if check_db $stap_user_ssl_db/client $EUID $USER; then local_ssl_dbs=$stap_user_ssl_db/client fi fi fi # Additional location for all users. Must be owned by root. if test -e $stap_root_ssl_db/client; then if check_db $stap_root_ssl_db/client 0 root; then public_ssl_dbs=$stap_root_ssl_db/client fi fi # Default options settings p_phase=5 v_level=0 keep_temps=0 m_name= module_name=stap_$$ uname_r="`uname -r`" arch=`stap_get_arch` # Default variable settings find_all= # 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 $stap_tmpdir_prefix_client.XXXXXX` || \ fatal "Cannot create temporary directory " $tmpdir_client tmpdir_env=`dirname $tmpdir_client` session_only_prompt_done=0 } # 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 { # Each command line argument will be written to its own file within the # request package. argc=1 packed_options='-' arg_subst= while test $# != 0 do advance=0 dash_seen=0 client_arg=0 # Start of a new token. first_token="$1" until test $advance != 0 do # Identify the next option first_char="${first_token:0:1}" second_char= 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. # Is it a long option (i.e. --option)? second_char="${first_token:1:1}" if test "X$second_char" = "X-"; then case "$first_token" in --ssl=*) process_ssl "$first_token" ;; --server=*) process_server "$first_token" ;; *) # An unknown or unimportant option. Ignore it. ;; esac advance=$(($advance + 1)) break fi # It's not a lone dash, or a long option, so it's a short option string. # Remove the dash. first_token="${first_token:1}" dash_seen=1 first_char="${first_token:0:1}" fi fi if test $dash_seen = 0; then # The dash has not been seen. This is either the script file # name or an argument 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" script_file_argc=$argc fi advance=$(($advance + 1)) break fi fi # We are at the start of an option. Look at the first character. case $first_char in a) get_arg "$first_token" "$2" process_a "$stap_arg" ;; B) get_arg "$first_token" "$2" ;; c) get_arg "$first_token" "$2" process_c "$stap_arg" ;; D) get_arg "$first_token" "$2" ;; 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" p_phase=2 ;; L) get_arg "$first_token" "$2" p_phase=2 ;; m) get_arg "$first_token" "$2" process_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" process_r "$stap_arg" ;; R) get_arg "$first_token" "$2" process_R "$stap_arg" ;; s) get_arg "$first_token" "$2" ;; S) get_arg "$first_token" "$2" ;; v) v_level=$(($v_level + 1)) ;; x) get_arg "$first_token" "$2" ;; *) # An unknown or unimportant flag. ;; esac if test $advance = 0; then # Just another flag character. Consume it. first_token="${first_token:1}" if test "X$first_token" = "X"; then advance=$(($advance + 1)) fi fi done # Consume the arguments we just processed. while test $advance != 0; do # Does the final argument file contain a client-side file # name which must be changed to a server-side name? local arg if test "X$arg_subst" != "X" -a $advance = 1; then arg="$arg_subst" arg_subst= else arg="$1" fi # If it's not client-only argument, # place the argument in a numbered file within our temp # directory. # o We don't write a newline at the end, since newline could be # part of the argument. # o We add an X to the beginning of the file # in order to avoid having 'echo' interpret the output as # its own option. We then remove the X. # There must be a better way. if test $client_arg = 0; then echo -n "X$arg" > "$tmpdir_client/argv$argc" sed -i "s|^X||" "$tmpdir_client/argv$argc" argc=$(($argc + 1)) fi # Get the next argument. shift advance=$(($advance - 1)) packed_options='-' done done # If the script file was given and it's not '-', then replace it with its # client-temp-name in its argument file. if test "X$script_file" != "X"; then local local_name if test "$script_file" != "-"; then generate_client_temp_name "$script_file" local_name="$client_temp_name" else local_name="-" fi echo -n "script/$local_name" > "$tmpdir_client/argv$script_file_argc" fi # Processing based on final options settings # Complete the list of local certificate databases local_ssl_dbs="$additional_local_ssl_dbs $local_ssl_dbs" # We must have at least one usable certificate database. test "X$local_ssl_dbs" != "X " -o "X$public_ssl_dbs" != "X" || \ fatal "No usable certificate databases found" } # function: get_arg FIRSTWORD SECONDWORD # # Collect an argument to the given option function get_arg { # Remove first character. local opt="${1:0:1}" local first="${1:1}" packed_options="${packed_options}$opt" # Advance to the next token, if the first one is exhausted. if test "X$first" = "X"; then advance=$(($advance + 1)) first="$2" fi stap_arg="$first" test "X$first" != "X" && advance=$(($advance + 1)) } # function: process_ssl ARGUMENT # # Process the --ssl option. function process_ssl { client_arg=1 local db="${1:6}" test "X$db" != "X" || \ fatal "Missing argument to --ssl" check_db "$db" || return additional_local_ssl_dbs="$additional_local_ssl_dbs $db" } # function: process_server ARGUMENT # # Process the --server option. function process_server { client_arg=1 local spec="${1:9}" test "X$spec" != "X" || \ fatal "Missing argument to --server" specified_servers="$specified_servers $spec" } # function: process_c ARGUMENT # # Process the -c flag. function process_c { c_cmd="$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 } # function: process_I ARGUMENT ORIGINAL_ARGUMENT # # Process the -I flag. function process_I { test "X$1" = "X" && return test "${1:0:1}" = " " && return include_file_or_directory tapsets "$1" if test $advance = 1; then arg_subst="${packed_options}tapsets/$included_name" else arg_subst="tapsets/$included_name" fi } # function: process_m ARGUMENT # # Process the -m flag. function process_m { module_name="$1" m_name="$1" } # function: process_o ARGUMENT # # Process the -o flag. function process_o { stdout_redirection="$1" } # function: process_p ARGUMENT # # Process the -p flag. function process_p { p_phase="$1" } # function: process_r ARGUMENT # # Process the -r flag. function process_r { local first_char="${1:0:1}" if test "$first_char" = "/"; then # fully specified path kernel_build_tree="$1" version_file_name="$kernel_build_tree/include/config/kernel.release" # The file include/config/kernel.release within the # build tree is used to pull out the version information release=`cat "$version_file_name" 2>/dev/null` if test "X$release" = "X"; then fatal "Missing $version_file_name" return fi else # kernel release specified directly release="$1" fi if test "X$release" != "X$uname_r"; then uname_r="$release" find_all="--all" fi } # function: process_a ARGUMENT # # Process the -a flag. function process_a { if test "X$1" != "X$arch"; then arch="$1" find_all="--all" fi } # function: process_R ARGUMENT ORIGINAL_ARGUMENT # # Process the -R flag. function process_R { test "X$1" = "X" && return test "${1:0:1}" = " " && return include_file_or_directory runtime "$1" if test $advance = 1; then arg_subst="${packed_options}runtime/$included_name" else arg_subst="runtime/$included_name" fi } # 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 and save it's name in the variable # included_name. We use a global variable instread of echoing the # result since the use of `include_file_or_directory` loses a trailing # newline. function include_file_or_directory { # Add a symbolic link of the named file or directory to our temporary # directory, but only if the file or directory exists. generate_client_temp_name "$2" local local_name="$client_temp_name" included_name="$local_name" test -e "/$local_name" || return local local_dirname=`dirname "$local_name"` mkdir -p "$tmpdir_client/$1/$local_dirname" || \ fatal "Could not create $tmpdir_client/$1/$local_dirname" ln -s "/$local_name" "$tmpdir_client/$1/$local_name" || \ fatal "Could not link $tmpdir_client/$1/$local_name to /$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 and stores it in the variable # client_temp_name. We use a global variable instread of echoing the # result since the use of `generate_client_temp_name` loses a trailing # newline. function generate_client_temp_name { # Transform the name into a fully qualified path name local full_name="$1" test "${full_name:0:1}" != "/" && full_name="$wd/$full_name" # The same name without the initial / or trailing / local local_name="${full_name:1}" test "${local_name: -1:1}" = "/" && local_name="${local_name:0:$((${#local_name}-1))}" client_temp_name="$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 "Cannot create temporary directory " $tmpdir_client/script cat > "$tmpdir_client/script/$script_file" else include_file_or_directory script "$script_file" fi fi # Add the necessary info to special files in our temporary directory. echo "sysinfo: `client_sysinfo`" > sysinfo } # function client_sysinfo # # Generate the client's sysinfo and echo it to stdout function client_sysinfo { echo "$uname_r $arch" } # 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 zip file cd $tmpdir_env local tmpdir_client_base=`basename $tmpdir_client` zip_client=$tmpdir_env/`mktemp $tmpdir_client_base.zip.XXXXXX` || \ fatal "Cannot create temporary file " $zip_client cd $tmpdir_client (rm -f $zip_client && zip -r $zip_client * > /dev/null) || \ fatal "zip of request tree, $tmpdir_client, failed" } # function: unpack_response # # Unpack the zip file received from the server and make the contents available # for printing the results and/or running 'staprun'. function unpack_response { tmpdir_server=`mktemp -dt $stap_tmpdir_prefix_client.server.XXXXXX` || \ fatal "Cannot create temporary file " $tmpdir_server # Unpack the server output directory unzip -d $tmpdir_server $zip_server > /dev/null || \ fatal "Cannot unpack server response, $zip_server" # Check the contents of the directory. It should contain: # 1) a file called stdout # 2) a file called stderr # 3) a file called rc # 4) optionally a directory named to match stap?????? local num_files=`ls $tmpdir_server | wc -l` test $num_files = 4 -o $num_files = 3 || \ fatal "Wrong number of files in server's temp directory" test -f $tmpdir_server/stdout || \ fatal "`pwd`/$tmpdir_server/stdout does not exist or is not a regular file" test -f $tmpdir_server/stderr || \ fatal "`pwd`/$tmpdir_server/stderr does not exist or is not a regular file" test -f $tmpdir_server/rc || \ fatal "`pwd`/$tmpdir_server/rc does not exist or is not a regular file" # See if there is a systemtap temp directory. There should be at least an empty one. # ls -l $tmpdir_server tmpdir_stap=`cd $tmpdir_server && ls | grep stap......\$ 2>/dev/null` if test "X$tmpdir_stap" != "X"; then test -d $tmpdir_server/$tmpdir_stap || \ fatal "$tmpdir_server/$tmpdir_stap is not a directory" # Move the systemtap temp directory to a local temp location, if -k # was specified. if test $keep_temps = 1; then local local_tmpdir_stap=`mktemp -dt stapXXXXXX` || \ fatal "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=$tmpdir_server/$tmpdir_stap # Make sure we own the systemtap temp directory if we are root. test $EUID = 0 && chown $EUID:$EUID $tmpdir_stap fi fi if test $keep_temps = 0; then # Remove the output line due to the synthetic server-side -k sed -i "/^Keeping temporary directory.*/ d" $tmpdir_server/stderr fi if test $p_phase = 5; then # Remove the output line due to the synthetic server-side -p4 sed -i "/^.*\.ko$/ d" $tmpdir_server/stdout fi } # function: find_and_connect_to_server # # Find and establish connection with a compatible stap server. function find_and_connect_to_server { local num_servers=0 # Make a place to receive the response file. zip_server=`mktemp -t $stap_tmpdir_prefix_client.server.zip.XXXXXX` || \ fatal "Cannot create temporary file " $zip_server # If servers were specified on the command line, then try them # in sequence. Don't try any other servers. if test "X$specified_servers" != "X"; then for server in $specified_servers; do num_servers=$(($num_servers + 1)) # If the server is completely specified, (i.e. server:port), # then try it directly. port=`expr "$server" : '.\+:\([0-9]\+\)'` if test "X$port" != "X"; then name=`expr "$server" : '\(.\+\):[0-9]\+'` # If we have been given an ip address, then try to resolve it to a name. # If we have been given a name, try to resolve the full name. # The full name is needed in order to validate the server's certificate. address=`expr "$name" : '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)'` if test "X$address" = "X"; then # We've been given a host name full_name=`nslookup $name | awk '/^Name\:/ {print $2}'` if test "X$full_name" != "X"; then name=$full_name fi else # We've been given an ip address. name=`nslookup $address | awk '/in-addr\.arpa/ {print $4}'` name=`expr "$name" : '\(.*\)\.'` if test "X$name" = "X"; then echo "Cannot resolve ip address $address" >> "$tmpdir_client/connect" continue fi fi # Now try to contact the given server. send_receive $name $port && return continue fi # Otherwise select the matching server from the available servers # and use the port it is advertizing. # # Have we been given an ip address? If so, just use it. address=`expr "$server" : '\([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\)'` if test "X$address" = "X"; then # We have not been given an ip address. Try to resolve it as a host name. if test "X$server" = "Xlocalhost"; then # We don't want the address of the loopback interface here. Avahi will present # the actual ip address. server=$our_host_name$our_domain_name fi address=`nslookup $server | awk '/^Address\:[ \t][0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$/ {print $2}'` if test "X$address" = "X"; then echo "Cannot resolve server $server" >> "$tmpdir_client/connect" continue fi fi ${stap_pkglibexecdir}stap-find-servers $find_all | grep $address > "$tmpdir_client/servers" if test `wc -l "$tmpdir_client/servers" | awk '{print $1}'` = "0"; then warning "No server is available on $server" 2>> "$tmpdir_client/connect" continue fi choose_server && return done else # No servers specified. Find available servers and choose one of them. # Remember which ssl certificate database was used to authenticate the chosen # server. ${stap_pkglibexecdir}stap-find-servers $find_all > "$tmpdir_client/servers" choose_server && return num_servers=`wc -l "$tmpdir_client/servers" | awk '{print $1}'` fi if test $num_servers = 0; then fatal "Unable to find a server" fi test -f "$tmpdir_client/connect" && cat "$tmpdir_client/connect" >&2 fatal "Unable to connect to a server" } # function: choose_server # # Examine each line from "$tmpdir_client/servers" and attempt to connect to # each server specified until successful. function choose_server { local name ip port remain while read -u3 name ip port remain do if test "X$name" = "X"; then fatal "Server name not provided by avahi" fi # if test "X$ip" = "X"; then # fatal "Server ip address not provided by avahi" # fi if test "X$port" = "X"; then fatal "Server port not provided by avahi" fi # Does the server build for the kernel release and architecture that we want? sysinfo=`expr "$remain" : "'sysinfo=\\\(.*\\\)'"` test "X$sysinfo" != "X$uname_r $arch" && continue send_receive $name $port && return done 3< "$tmpdir_client/servers" # Could not connect to a server return 1 } # function: send_receive SERVER PORT # # Connect to the server, send the request and receive the response. function send_receive { local server="$1" local port="$2" # The server must match the dns name on the certificate # and must be 'localhost' if the server is on the local host. local server_host_name=`expr "$server" : "\\\([a-zA-Z0-9-]*\\\).*"` local server_domain_name=`expr "$server" : "$server_host_name\\\(.*\\\)"` if test "X$server_domain_name" = "X.local"; then server_domain_name=$our_domain_name fi if test "X$server_host_name$server_domain_name" = "Xlocalhost$our_domain_name"; then server=localhost elif test "X$server_host_name$server_domain_name" = "X$our_host_name$our_domain_name"; then server=localhost else server=$server_host_name$server_domain_name fi # Try to connect using each of the given local certificate databases in turn # for verification. local rc for ssl_db in $local_ssl_dbs do # Send the request and receive the response using stap-client-connect attempt_connection -i $zip_client -o $zip_server -d $ssl_db -p $port -h $server && return # Try the next database, but give the server a chance to reset. sleep 1 done # Next, try the public certificate databases. for ssl_db in $public_ssl_dbs do # Send the request and receive the response using stap-client-connect attempt_connection -i $zip_client -o $zip_server -d $ssl_db -p $port -h $server && return # Try the next database, but give the server a chance to reset. sleep 1 done # Could not connect using any of the certificate databases return 1 } # function: attempt_connection ARGS # # Attempt connection with the given server. Give the user a chance to # trust the server if it is not already trusted function attempt_connection { echo "Attempting connection with $server:$port using certificate database in '$ssl_db'" >> "$tmpdir_client/connect" # Send the request and receive the response using stap-client-connect ${stap_pkglibexecdir}stap-client-connect "$@" >> "$tmpdir_client/connect" 2>&1 & wait '%${stap_pkglibexecdir}stap-client-connect' local rc=$? test $rc = 0 && return # The connection failed. If it was because the server is not trusted, give # the user a chance to decide whether to trust the server anyway. # The prompt will not be printed and the read will quickly timeout if # stdin is not from a terminal. if test $rc = 2; then # Output any connection messages generated thus far cat "$tmpdir_client/connect" >&2 rm "$tmpdir_client/connect" local response local prompt="The server at $server:$port is not trusted based on the certificate database in '$ssl_db' " if test $session_only_prompt_done = 0; then session_only_prompt_done=1 prompt="${prompt}Trust this server for for this session only? [y/N] " read -t 30 -p "$prompt" response || echo n if test "$response" = "y" -o "$response" = "Y"; then ${stap_pkglibexecdir}stap-client-connect "$@" -t session >> "$tmpdir_client/connect" 2>&1 & wait '%${stap_pkglibexecdir}stap-client-connect' test $? = 0 && return return 1 # Connection failed fi prompt= fi if test "$ssl_db" = "$stap_root_ssl_db/client"; then prompt="${prompt}Adding this server's certificate to this database will make this server trusted by all users on the local host. " fi prompt="${prompt}Add this server's certificate to '$ssl_db'? [y/N] " read -t 30 -p "$prompt" response || echo n if test "$response" = "y" -o "$response" = "Y"; then ${stap_pkglibexecdir}stap-client-connect "$@" -t permanent >> "$tmpdir_client/connect" 2>&1 & wait '%${stap_pkglibexecdir}stap-client-connect' test $? = 0 && return fi fi # Connection failed return 1 } # function: process_response # # Write the stdout and stderr from the server to stdout and stderr respectively. function process_response { # Pick up the results of running stap on the server. cd $tmpdir_server rc=`cat rc` if test $p_phase -ge 4; then if test -f $tmpdir_stap/*.ko; then if test $p_phase = 4 -o "X$m_name" != "X"; then cp -p $tmpdir_stap/*.ko $wd/$module_name.ko test -f $tmpdir_stap/*.sgn && cp -p $tmpdir_stap/*.sgn $wd/$module_name.ko.sgn else module_name=`ls $tmpdir_stap/*.ko` module_name=`expr "$module_name" : '\(.*\)\.ko'` fi elif test "X$script_file" != "X" -o "X$e_script" != "X"; then if test "X$rc" != "X" -a $rc = 0; then stream_output fatal "no module returned by the server" fi fi fi # Change the name of the temp directory and module name in stdout and stderr sed -i "s,stap_[0-9]\+,$module_name,g" $tmpdir_server/stdout sed -i "s,stap_[0-9]\+,$module_name,g" $tmpdir_server/stderr sed -i "s,into \".*$module_name,into \"$module_name,g" $tmpdir_server/stdout sed -i "s,into \".*$module_name,into \"$module_name,g" $tmpdir_server/stderr # Output stdout and stderr as directed stream_output } # function: stream_output # # Output stdout and stderr as directed function stream_output { cd $tmpdir_server cat stderr >&2 cat stdout } # function: maybe_call_staprun # # Call staprun using the module returned from the server, if requested. function maybe_call_staprun { if test $rc != 0; then # stap run on the server failed, so don't bother return fi if test $p_phase -ge 4; then # There should be a systemtap temporary directory. if test "X$tmpdir_stap" = "X"; then # OK if no script specified if test "X$e_script" != "X" -o "X$script_file" != "X"; then fatal "systemtap temporary directory is missing in server response" fi return fi if test $p_phase = 5; then test $v_level -gt 0 && echo "Pass 5: starting run." >&2 # We have a module. Try to run it # If a -c command was specified, pass it along. if test "X$c_cmd" != "X"; then staprun_opts="-c '$c_cmd'" fi # The -v level will be one less than what was specified # for us. for ((vl=$((v_level - 1)); $vl > 0; --vl)) do staprun_opts="$staprun_opts -v" done # if -o was specified, pass it along if test "X$stdout_redirection" != "X"; then staprun_opts="$staprun_opts -o $stdout_redirection" fi # Run it from our original working directory cd $wd # Run it in the background and wait for it. This # way any signals sent to us can be caught. if test $v_level -ge 2; then echo "running `staprun_PATH` $staprun_opts $module_name.ko" >&2 fi eval `staprun_PATH` "$staprun_opts" $module_name.ko rc=$? # Wait until the job actually disappears so that its output is complete. while jobs '%?staprun' >/dev/null 2>&1 do sleep 1 done test $v_level -gt 0 && echo "Pass 5: run completed in 0usr/0sys/0real ms." >&2 fi fi } # function: staprun_PATH # # Compute a PATH suitable for running staprun. function staprun_PATH { # If $SYSTEMTAP_STAPRUN is set, then use that if test "X$SYSTEMTAP_STAPRUN" != "X"; then echo $SYSTEMTAP_STAPRUN return fi # Otherwise, if there is an exec_prefix, then use it. if test "X$stap_exec_prefix" != "X"; then echo ${stap_exec_prefix}staprun return fi # Otherwise, we have been called by the dejagnu test harness as 'stap' # and we are the first 'stap' on the path. Since staprun may call # 'stap', remove the PATH component where we live from the PATH in order to # avoid recursion. local first_stap=`which stap` local PATH_component=`dirname $first_stap` echo "PATH=$PATH staprun" | sed "s,$PATH_component,,g" } # function: check_db DBNAME [ EUID USER ] # # Check the security of the given database directory. function check_db { local dir="$1" local euid="$2" local user="$3" local rc=0 # Check that we have been given a directory if ! test -e $dir; then warning "Certificate database '$dir' does not exist" return 1 fi if ! test -d $dir; then warning "Certificate database '$dir' is not a directory" return 1 fi # If euid has been specified, then this directory must be owned by that # user. if test "X$euid" != "X"; then local ownerid=`stat -c "%u" $dir` if test "X$ownerid" != "X$euid"; then warning "Certificate database '$dir' must be owned by $user" rc=1 fi fi # Check that we can read the directory if ! test -r $dir; then warning "Certificate database '$dir' is not readble" rc=1 fi # Check the access permissions of the directory local perm=0`stat -c "%a" $dir` if test $((($perm & 0400) == 0400)) = 0; then warning "Certificate database '$dir' should be readable by the owner" fi if test $((($perm & 0200) == 0200)) = 0; then warning "Certificate database '$dir' should be writeable by the owner" fi if test $((($perm & 0100) == 0100)) = 0; then warning "Certificate database '$dir' should be searchable by the owner" fi if test $((($perm & 0040) == 0040)) = 0; then warning "Certificate database '$dir' should be readable by the group" fi if test $((($perm & 0020) == 0020)) = 1; then warning "Certificate database '$dir' must not be writable by the group" rc=1 fi if test $((($perm & 0010) == 0010)) = 0; then warning "Certificate database '$dir' should be searchable by the group" fi if test $((($perm & 0004) == 0004)) = 0; then warning "Certificate database '$dir' should be readable by others" fi if test $((($perm & 0002) == 0002)) = 1; then warning "Certificate database '$dir' must not be writable by others" rc=1 fi if test $((($perm & 0001) == 0001)) = 0; then warning "Certificate database '$dir' should be searchable by others" fi # Now check the permissions of the critical files. check_db_file $dir/cert8.db $euid $user || rc=1 check_db_file $dir/key3.db $euid $user || rc=1 check_db_file $dir/secmod.db $euid $user || rc=1 test $rc = 1 && warning "Unable to use certificate database '$dir' due to errors" return $rc } # function: check_db_file FILENAME [ EUID USER ] # # Check the security of the given database file. function check_db_file { local file="$1" local rc=0 # Check that we have been given a file if ! test -e $file; then warning "Certificate database file '$file' does not exist" return 1 fi if ! test -f $file; then warning "Certificate database file '$file' is not a regular file" return 1 fi # If euid has been specified, then this directory must be owned by that # user. if test "X$euid" != "X"; then local ownerid=`stat -c "%u" $file` if test "X$ownerid" != "X$euid"; then warning "Certificate database file '$file' must be owned by $user" rc=1 fi fi # Check that we can read the file if ! test -r $file; then warning "Certificate database file '$file' is not readble" rc=1 fi # Check the access permissions of the file local perm=0`stat -c "%a" $file` if test $((($perm & 0400) == 0400)) = 0; then warning "Certificate database file '$file' should be readable by the owner" fi if test $((($perm & 0200) == 0200)) = 0; then warning "Certificate database file '$file' should be writeable by the owner" fi if test $((($perm & 0100) == 0100)) = 1; then warning "Certificate database file '$file' must not be executable by the owner" rc=1 fi if test $((($perm & 0040) == 0040)) = 0; then warning "Certificate database file '$file' should be readable by the group" fi if test $((($perm & 0020) == 0020)) = 1; then warning "Certificate database file '$file' must not be writable by the group" rc=1 fi if test $((($perm & 0010) == 0010)) = 1; then warning "Certificate database file '$file' must not be executable by the group" rc=1 fi if test $((($perm & 0004) == 0004)) = 0; then warning "Certificate database file '$file' should be readable by others" fi if test $((($perm & 0002) == 0002)) = 1; then warning "Certificate database file '$file' must not be writable by others" rc=1 fi if test $((($perm & 0001) == 0001)) = 1; then warning "Certificate database file '$file' must not be executable by others" rc=1 fi return $rc } # function: warning [ MESSAGE ] # # Warning error # Prints its arguments to stderr function warning { echo "$0: WARNING:" "$@" >&2 } # function: fatal [ MESSAGE ] # # Fatal error # Prints its arguments to stderr and exits function fatal { echo "$0: ERROR:" "$@" >&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 $zip_client rm -f $zip_server rm -fr $tmpdir_server fi } # function: terminate # # Terminate gracefully. function terminate { # Clean up echo "$0: terminated by signal" >&2 cleanup # Kill any running staprun job kill -s SIGTERM '%?staprun' 2>/dev/null # Kill any stap-client-connect job kill -s SIGTERM '%${stap_pkglibexecdir}stap-client-connect' 2>/dev/null exit 1 } # function: interrupt # # Pass an interrupt (ctrl-C) to staprun function interrupt { # Kill any stap-client-connect job # SIGINT won't do it. kill -s SIGTERM '%${stap_pkglibexecdir}stap-client-connect' 2>/dev/null # If staprun was not running, then exit. cleanup exit 1 } # function: ignore_signal # # Called in order to ignore a signal function ignore_signal { : } #----------------------------------------------------------------------------- # Beginning of main line execution. #----------------------------------------------------------------------------- initialization parse_options "$@" create_request package_request find_and_connect_to_server unpack_response process_response maybe_call_staprun cleanup exit $rc