#!/bin/bash

# Find compile servers for systemtap
#
# Copyright (C) 2008, 2009 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 uses avahi to find systemtap compile servers on the local
# network. Information about each server found is printed to stdout.

# Initialize the environment
. ${PKGLIBEXECDIR}stap-env

#-----------------------------------------------------------------------------
# Helper functions.
#-----------------------------------------------------------------------------
# function: initialization
function initialization {
    rc=1 # not found yet
    if test "X$1" = "X--all"; then
	find_all=1
    else
	find_all=0
    fi
    timeout=20 # seconds
}

# function: find_servers
#
# Find and establish connection with a compatible stap server.
function find_servers {
    # Create a temp file for the list of servers. We do this instead
    # of using a pipe so that we can kill avahi-browse if it
    # takes too long.
    tmpfile=`mktemp -t stap-serversXXXXXX` || \
	fatal "Cannot create temporary file " $tmpfile

    # Find servers
    avahi-browse $stap_avahi_service_tag --terminate -r > $tmpfile &
    
    for ((attempt=0; $attempt < $timeout; ++attempt))
    do
	if ! jobs '%avahi-browse' >/dev/null 2>&1; then
	    break
	fi
	sleep 1
    done

    # Kill avahi-browse, if it's still running
    test $attempt = $timeout && kill -s SIGTERM '%avahi-browse' 2>/dev/null

    match_server < $tmpfile
    rm -fr $tmpfile
}

# function: match_server
#
# Find a suitable server using the avahi-browse output provided on stdin.
function match_server {
    local server_ip
    local server_name
    local server_sysinfo
    local server_port

    rc=1 # not found yet

    # Loop over the avahi service descriptors.
    read -t $timeout || return
    while test "X$REPLY" != "X"
    do
	server_name=
	server_ip=
	server_port=
	server_sysinfo=

	# Examine the next service descriptor
	# Is it a stap server?
	if ! echo $REPLY | grep -q "=.* .* IPv4 .*_stap"; then
	    read -t $timeout || return
	    continue
	fi
	REPLY=
	
	# Get the details of the service
	local service_tag equal service_data
	while read -t $timeout service_tag equal service_data
	do
	    case $service_tag in
		hostname )
		    server_name=`expr "$service_data" : '\[\([^]]*\)\]'`
		    ;;
		address )
		    # Sometimes (seems random), avahi-resolve-host-name resolves a local server to its
		    # hardware address rather than its ip address. Keep trying until we get
		    # an ip address.
		    server_ip=`expr "$service_data" : '\[\([^]]*\)\]'`
		    local attempt
		    for ((attempt=0; $attempt < 5; ++attempt))
		    do
			server_ip=`expr "$server_ip" : '\([0-9]*\.[0-9]*\.[0-9]*\.[0-9]*\)$'`
			if test "X$server_ip" != "X"; then
			    break
			fi
		        # Resolve the server.domain to an ip address.
			server_ip=`avahi-resolve-host-name $server_name`
			server_ip=`expr "$server_ip" : '.*	\(.*\)$'`
		    done
		    ;;
		port )
		    server_port=`expr "$service_data" : '\[\([^]]*\)\]'`
		    ;;
		txt )
		    server_sysinfo=`expr "$service_data" : '\[.*\"\(sysinfo=[^]]*\)\"\]'`
		    ;;
		* )
		    REPLY="$service_tag $equal $service_data"
		    break
		    ;;
	    esac
	done

	# It's an stap server, but is it compatible?
	if test $find_all = 0 -a "$server_sysinfo" != "`client_sysinfo`"; then
	    continue
	fi

	# It's compatible, or we're finding all servers. Print a summary line
	echo "$server_name $server_ip $server_port '$server_sysinfo'"
	rc=0
    done
}

# function client_sysinfo
#
# Generate the client's sysinfo and echo it to stdout
function client_sysinfo {
    if test "X$sysinfo_client" = "X"; then
	# Add some info from uname
	sysinfo_client="`uname -r` `stap_get_arch`"
    fi
    echo sysinfo=$sysinfo_client
}

# function: fatal [ MESSAGE ]
#
# Fatal error
# Prints its arguments to stderr and exits
function fatal {
    echo "$0: ERROR:" "$@" >&2
    exit 1
}

#-----------------------------------------------------------------------------
# Beginning of main line execution.
#-----------------------------------------------------------------------------
initialization "$@"
find_servers

exit $rc
