This is the mail archive of the systemtap@sourceware.org mailing list for the systemtap project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Compile Server Merge into stap - Patch for Feedback


Hi,

This is a patch which merges the functionality of the stap-client and stap-find-servers scripts into stap. This is part of our efforts to make the compile client/server implementation more robust. This has already undergone one round of review here at Red Hat and I would like to solicit feedback from interested parties on this list as well.

With this patch:

o Basic client functionality is available in stap, meaning that stap can automatically find a compatible server (if any are available), use it to compile a script and execute it locally (if requested). Where you would have used 'stap-client ...' you now use 'stap --server ...'.

o Other stap-client options are yet to be fleshed out (e.g. --server=XXX, --ssl=XXX).

o Server status information is available from stap. Where you would previously use 'stap-find-servers' you now use 'stap --server-status'.

o For now, stap-client and stap-find-servers still work as before. They will eventually either go away or remain as (very) thin wrappers around the new implementation.

Clearly there is more work to do, but I want to make sure this is headed in the right direction.

Feedback please!

Thanks,
Dave
diff --git a/Makefile.am b/Makefile.am
index d8dfbe9..d9317fc 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -49,7 +49,7 @@ stap_SOURCES = main.cxx session.cxx \
 	tapset-been.cxx tapset-procfs.cxx tapset-timers.cxx \
 	tapset-perfmon.cxx tapset-mark.cxx tapset-itrace.cxx \
 	tapset-utrace.cxx task_finder.cxx dwflpp.cxx rpm_finder.cxx \
-	setupdwfl.cxx
+	setupdwfl.cxx csclient.cxx
 stap_LDADD = @stap_LIBS@ @sqlite3_LIBS@
 stap_DEPENDENCIES =
 endif
@@ -100,7 +100,18 @@ stap_CXXFLAGS = $(AM_CXXFLAGS) @PIECXXFLAGS@
 stap_CPPFLAGS = $(AM_CPPFLAGS) -DSTAP_SDT_V2
 stap_LDFLAGS = $(AM_LDFLAGS) @PIELDFLAGS@
 
+if HAVE_AVAHI
+stap_CXXFLAGS += $(avahi_CFLAGS)
+stap_CPPFLAGS += $(avahi_CFLAGS)
+stap_LDADD += -lavahi-client
+endif
+
 if HAVE_NSS
+stap_SOURCES += stap-client-connect.c nsscommon.c
+stap_CFLAGS += $(nss_CFLAGS) $(nspr_CFLAGS) -DSTAP
+stap_CXXFLAGS += $(nss_CFLAGS) $(nspr_CFLAGS)
+stap_CPPFLAGS += $(nss_CFLAGS) $(nspr_CFLAGS)
+stap_LDADD += -lssl3 -lnss3 -lnspr4 -lplc4
 stap_client_connect_LDFLAGS = $(AM_LDFLAGS)
 stap_server_connect_LDFLAGS = $(AM_LDFLAGS)
 stap_sign_module_LDFLAGS = $(AM_LDFLAGS)
diff --git a/configure.ac b/configure.ac
index e412ad5..87b7ac4 100644
--- a/configure.ac
+++ b/configure.ac
@@ -301,33 +301,53 @@ dnl Find the location of nss and nspr headers and certutil
     AC_CHECK_FILE([/usr/include/nspr], nsprdir=nspr)
   ])
   if test "x$nssdir" != "x" -a "x$nsprdir" != "x"; then
-  nss_CFLAGS="-I/usr/include/$nssdir"
-  nspr_CFLAGS="-I/usr/include/$nsprdir"
-  AC_SUBST(nss_CFLAGS)
-  AC_SUBST(nspr_CFLAGS)
-  save_CPPFLAGS="$CPPFLAGS"
-  CPPFLAGS="$CFLAGS $nss_CFLAGS $nspr_CFLAGS"
-  have_nss_includes=yes
-  AC_CHECK_HEADERS(["$nsprdir/nspr.h" "$nsprdir/plgetopt.h" "$nsprdir/prerror.h" "$nssdir/ssl.h" "$nssdir/nss.h" "$nssdir/pk11func.h" "$nssdir/secerr.h"], [], have_nss_includes=no)
-  CPPFLAGS="$save_CPPFLAGS"
-  have_nss_libs=no
-  AC_CHECK_LIB(nspr4, PR_Connect, [
-    AC_CHECK_LIB(ssl3, SSL_ReHandshake, have_nss_libs=yes)
-  ])
+    nss_CFLAGS="-I/usr/include/$nssdir"
+    nspr_CFLAGS="-I/usr/include/$nsprdir"
+    AC_SUBST(nss_CFLAGS)
+    AC_SUBST(nspr_CFLAGS)
+    save_CPPFLAGS="$CPPFLAGS"
+    CPPFLAGS="$CFLAGS $nss_CFLAGS $nspr_CFLAGS"
+    have_nss_includes=yes
+    AC_CHECK_HEADERS(["$nsprdir/nspr.h" "$nsprdir/plgetopt.h" "$nsprdir/prerror.h" "$nssdir/ssl.h" "$nssdir/nss.h" "$nssdir/pk11func.h" "$nssdir/secerr.h"], [], have_nss_includes=no)
+    CPPFLAGS="$save_CPPFLAGS"
+    have_nss_libs=no
+    AC_CHECK_LIB(nspr4, PR_Connect, [
+      AC_CHECK_LIB(ssl3, SSL_ReHandshake, have_nss_libs=yes)
+    ])
   fi
   if test "x${have_nss_includes}${have_nss_libs}${have_certutil}" != "xyesyesyes"; then
     if test "$enable_server" == "yes"; then
-      AC_MSG_ERROR([cannot find all libraries or tools for stap-server])
+      AC_MSG_ERROR([cannot find all libraries or tools for systemtap compile client or server])
     fi
     if test "$enable_server" == "check"; then
-      AC_MSG_WARN([will not build stap-server, cannot find all libraries or tools])
+      AC_MSG_WARN([will not build systemtap client or server, cannot find all libraries or tools])
     fi
   else
       AC_DEFINE([HAVE_NSS], [1], [Define to 1 if you have the nss libraries.])
   fi
+dnl Find the location of avahi-client headers and library
+  AC_CHECK_FILE([/usr/include/avahi-client], avahi_clientdir=avahi-client)
+  AC_CHECK_FILE([/usr/include/avahi-common], avahi_commondir=avahi-common)
+  if test "x$avahi_clientdir" != "x" -a "x$avahi_commondir" != "x"; then
+    have_avahi_includes=yes
+    AC_CHECK_HEADERS(["$avahi_clientdir/client.h" "$avahi_clientdir/lookup.h" "$avahi_commondir/simple-watch.h" "$avahi_commondir/malloc.h" "$avahi_commondir/error.h"], [], have_avahi_includes=no)
+    have_avahi_libs=no
+    AC_CHECK_LIB(avahi-client, avahi_client_new, have_avahi_libs=yes)
+  fi
+  if test "x${have_avahi_includes}${have_avahi_libs}" != "xyesyes"; then
+    if test "$enable_server" == "yes"; then
+      AC_MSG_ERROR([cannot find all libraries or tools for systemtap compile client or server])
+    fi
+    if test "$enable_server" == "check"; then
+      AC_MSG_WARN([will not build systemtap internal client or server, cannot find all libraries or tools])
+    fi
+  else
+      AC_DEFINE([HAVE_AVAHI], [1], [Define to 1 if you have the avahi libraries.])
+  fi
 fi
 AM_CONDITIONAL([BUILD_SERVER], [test "x${have_nss_includes}${have_nss_libs}${have_certutil}" == "xyesyesyes" -a "$enable_server" != "no"])
 AM_CONDITIONAL([HAVE_NSS],     [test "x${have_nss_includes}${have_nss_libs}${have_certutil}" == "xyesyesyes"])
+AM_CONDITIONAL([HAVE_AVAHI],   [test "x${have_avahi_includes}${have_avahi_libs}" == "xyesyes"])
 
 dnl Handle the optional grapher
 AC_ARG_ENABLE([grapher],
diff --git a/csclient.cxx b/csclient.cxx
new file mode 100644
index 0000000..44a5f10
--- /dev/null
+++ b/csclient.cxx
@@ -0,0 +1,1111 @@
+/*
+ Compile server client functions
+ Copyright (C) 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.
+*/
+#include "config.h"
+#include "session.h"
+#include "csclient.h"
+#include "util.h"
+#include "sys/sdt.h"
+
+#include <sys/times.h>
+#include <vector>
+#include <fstream>
+#include <sstream>
+#include <iostream>
+
+#if HAVE_AVAHI
+extern "C" {
+#include <avahi-client/client.h>
+#include <avahi-client/lookup.h>
+
+#include <avahi-common/simple-watch.h>
+#include <avahi-common/malloc.h>
+#include <avahi-common/error.h>
+#include <avahi-common/timeval.h>
+
+#if HAVE_NSS
+#include <ssl.h>
+#include <nspr.h>
+#include <nss.h>
+#include <pk11pub.h>
+#include <prerror.h>
+#include <secerr.h>
+#include <sslerr.h>
+
+#include "nsscommon.h"
+#endif
+
+#include <glob.h>
+}
+#endif
+
+#include <cstdlib>
+#include <cstdio>
+
+using namespace std;
+
+int
+compile_server_client::passes_0_4 ()
+{
+  STAP_PROBE1(stap, client__start, &s);
+
+#if ! HAVE_NSS
+  // This code will never be called, if we don't have NSS, but it must still
+  // compile.
+  int rc = 1; // Failure
+#else
+  // arguments parsed; get down to business
+  if (s.verbose > 1)
+    clog << "Using a compile server" << endl;
+
+  struct tms tms_before;
+  times (& tms_before);
+  struct timeval tv_before;
+  gettimeofday (&tv_before, NULL);
+
+  // Create the request package.
+  int rc = initialize ();
+  if (rc != 0 || pending_interrupts) goto done;
+  rc = create_request ();
+  if (rc != 0 || pending_interrupts) goto done;
+  rc = package_request ();
+  if (rc != 0 || pending_interrupts) goto done;
+
+  // Submit it to the server.
+  rc = find_and_connect_to_server ();
+  if (rc != 0 || pending_interrupts) goto done;
+
+  // Unpack and process the response.
+  rc = unpack_response ();
+  if (rc != 0 || pending_interrupts) goto done;
+  rc = process_response ();
+
+  if (rc == 0 && s.last_pass == 4)
+    {
+      cout << s.module_name + ".ko";
+      cout << endl;
+    }
+
+ done:
+  struct tms tms_after;
+  times (& tms_after);
+  unsigned _sc_clk_tck = sysconf (_SC_CLK_TCK);
+  struct timeval tv_after;
+  gettimeofday (&tv_after, NULL);
+
+#define TIMESPRINT "in " << \
+           (tms_after.tms_cutime + tms_after.tms_utime \
+            - tms_before.tms_cutime - tms_before.tms_utime) * 1000 / (_sc_clk_tck) << "usr/" \
+        << (tms_after.tms_cstime + tms_after.tms_stime \
+            - tms_before.tms_cstime - tms_before.tms_stime) * 1000 / (_sc_clk_tck) << "sys/" \
+        << ((tv_after.tv_sec - tv_before.tv_sec) * 1000 + \
+            ((long)tv_after.tv_usec - (long)tv_before.tv_usec) / 1000) << "real ms."
+
+  // syntax errors, if any, are already printed
+  if (s.verbose)
+    {
+      clog << "Compilation using a server completed "
+           << getmemusage()
+           << TIMESPRINT
+           << endl;
+    }
+#endif // HAVE_NSS
+
+  if (rc == 0)
+    {
+      // Save the module, if necessary.
+      if (s.last_pass == 4)
+	s.save_module = true;
+
+      // Copy module to the current directory.
+      if (s.save_module && ! pending_interrupts)
+	{
+	  string module_src_path = s.tmpdir + "/" + s.module_name + ".ko";
+	  string module_dest_path = s.module_name + ".ko";
+	  copy_file (module_src_path, module_dest_path, s.verbose > 1);
+	}
+    }
+
+  STAP_PROBE1(stap, client__end, &s);
+
+  return rc;
+}
+
+// Initialize a client/server session.
+int
+compile_server_client::initialize ()
+{
+  int rc = 0;
+
+  // Initialize session state
+  argc = 0;
+
+  // Default location for server certificates if we're not root.
+  uid_t euid = geteuid ();
+  if (euid != 0)
+    {
+      private_ssl_dbs.push_back (s.data_path + "/ssl/client");
+    }
+
+  // Additional location for all users.
+  public_ssl_dbs.push_back (SYSCONFDIR "/systemtap/ssl/client");
+
+  // Create a temporary directory to package things in.
+  client_tmpdir = s.tmpdir + "/client";
+  rc = create_dir (client_tmpdir.c_str ());
+  if (rc != 0)
+    {
+      const char* e = strerror (errno);
+      cerr << "ERROR: cannot create temporary directory (\""
+	   << client_tmpdir << "\"): " << e
+	   << endl;
+    }
+
+  return rc;
+}
+
+// Create the request package.
+int
+compile_server_client::create_request ()
+{
+  int rc = 0;
+
+  // Add the script file or script option
+  if (s.script_file != "")
+    {
+      if (s.script_file == "-")
+	{
+	  // Copy the script from stdin
+	  string packaged_script_dir = client_tmpdir + "/script";
+	  rc = create_dir (packaged_script_dir.c_str ());
+	  if (rc != 0)
+	    {
+	      const char* e = strerror (errno);
+	      cerr << "ERROR: cannot create temporary directory "
+		   << packaged_script_dir << ": " << e
+		   << endl;
+	      return rc;
+	    }
+	  rc = ! copy_file("/dev/stdin", packaged_script_dir + "/-");
+	  if (rc != 0)
+	    return rc;
+
+	  // Name the script in the packaged arguments.
+	  rc = add_package_arg ("script/-");
+	  if (rc != 0)
+	    return rc;
+	}
+      else
+	{
+	  // Add the script to our package. This will also name the script
+	  // in the packaged arguments.
+	  rc = include_file_or_directory ("script", s.script_file);
+	  if (rc != 0)
+	    return rc;
+	}
+    }
+
+  // Add -I paths. Skip the default directory.
+  if (s.include_arg_start != -1)
+    {
+      unsigned limit = s.include_path.size ();
+      for (unsigned i = s.include_arg_start; i < limit; ++i)
+	{
+	  rc = add_package_arg ("-I");
+	  if (rc != 0)
+	    return rc;
+	  rc = include_file_or_directory ("tapset", s.include_path[i]);
+	  if (rc != 0)
+	    return rc;
+	}
+    }
+
+  // Add other options.
+  rc = add_package_args ();
+  if (rc != 0)
+    return rc;
+
+  // Add the sysinfo file
+  string sysinfo = "sysinfo: " + s.kernel_release + " " + s.architecture;
+  rc = write_to_file (client_tmpdir + "/sysinfo", sysinfo);
+
+  return rc;
+}
+
+// Add the arguments specified on the command line to the server request
+// package, as appropriate.
+int
+compile_server_client::add_package_args ()
+{
+  int rc = 0;
+  unsigned limit = s.server_args.size();
+  for (unsigned i = 0; i < limit; ++i)
+    {
+      rc = add_package_arg (s.server_args[i]);
+      if (rc != 0)
+	return rc;
+    }
+
+  limit = s.args.size();
+  for (unsigned i = 0; i < limit; ++i)
+    {
+      rc = add_package_arg (s.args[i]);
+      if (rc != 0)
+	return rc;
+    }
+  return rc;
+}  
+
+// Symbolically link the given file or directory into the client's temp
+// directory under the given subdirectory.
+int
+compile_server_client::include_file_or_directory (
+  const string &subdir, const string &path, const char *option
+)
+{
+  char *cpath = 0;
+  string rpath;
+  vector<string> components;
+
+  // First ensure that the requested subdirectory exists.
+  string name = client_tmpdir + "/" + subdir;
+  int rc = create_dir (name.c_str ());
+  if (rc) goto done;
+
+  // Now canonicalize the given path and remove the leading /.
+  cpath = canonicalize_file_name (path.c_str ());
+  if (! cpath)
+    {
+      rc = 1;
+      goto done;
+    }
+  rpath = cpath + 1;
+
+  // Now ensure that each component of the path exists.
+  tokenize (rpath, components, "/");
+  assert (components.size () >= 1);
+  unsigned i;
+  for (i = 0; i < components.size() - 1; ++i)
+    {
+      name += "/" + components[i];
+      rc = create_dir (name.c_str ());
+      if (rc) goto done;
+    }
+
+  // Now make a symbolic link to the actual file or directory.
+  assert (i == components.size () - 1);
+  name += "/" + components[i];
+  rc = symlink (cpath, name.c_str ());
+  if (rc) goto done;
+
+  // Name this file or directory in the packaged arguments along with any
+  // associated option.
+  if (option)
+    {
+      rc = add_package_arg (option);
+      if (rc) goto done;
+    }
+
+  rc = add_package_arg (subdir + "/" + rpath);
+
+ done:
+  if (cpath)
+    free (cpath);
+
+  if (rc != 0)
+    {
+      const char* e = strerror (errno);
+      cerr << "ERROR: unable to add " << cpath << " to temp directory as "
+	   << name << ": " << e
+	   << endl;
+    }
+
+  return rc;
+}
+
+int
+compile_server_client::add_package_arg (const string &arg)
+{
+  int rc = 0;
+  ostringstream fname;
+  fname << client_tmpdir << "/argv" << ++argc;
+  write_to_file (fname.str (), arg); // NB: No terminating newline
+  return rc;
+}
+
+// Package the client's temp directory into a form suitable for sending to the
+// server.
+int
+compile_server_client::package_request ()
+{
+  // Package up the temporary directory into a zip file.
+  client_zipfile = client_tmpdir + ".zip";
+  string cmd = "cd " + client_tmpdir + " && zip -qr " + client_zipfile + " *";
+  int rc = stap_system (s.verbose, cmd);
+  return rc;
+}
+
+int
+compile_server_client::find_and_connect_to_server ()
+{
+  int rc;
+  int servers = 0;
+
+  unsigned limit = s.specified_servers.size ();
+  for (unsigned i = 0; i < limit; ++i)
+    {
+      const string &server = s.specified_servers[i];
+
+      // If the server string is empty, then work with all online, trusted and
+      // compatible servers.
+      if (server.empty ())
+	{
+	  vector<compile_server_info> default_servers;
+	  int pmask = compile_server_online | compile_server_trusted |
+	    compile_server_compatible;
+	  get_server_info (s, pmask, default_servers);
+
+	  // Try each server in succession until successful.
+	  unsigned limit = default_servers.size ();
+	  for (unsigned i = 0; i < limit; ++i)
+	    {
+	      ++servers;
+	      rc = compile_using_server (default_servers[i]);
+	      if (rc == 0)
+		return rc; // success!
+	    }
+	  continue;
+	}
+
+      // Otherwise work with the specified server
+      // XXXX: TODO
+    }
+
+  if (servers == 0)
+    cerr << "Unable to find a server" << endl;
+  else
+    cerr << "Unable to connect to a server" << endl;
+
+  return 1; // Failure
+}
+
+// Temporary until the stap-client-connect program goes away.
+extern "C"
+int
+client_main (const char *hostName, unsigned short port,
+	     const char* infileName, const char* outfileName);
+
+int 
+compile_server_client::compile_using_server (const compile_server_info &server)
+{
+  // This code will never be called if we don't have NSS, but it must still
+  // compile.
+#if HAVE_NSS
+  // Try resolve the host name if it is '.local'.
+  string::size_type dot_index = server.host_name.find ('.');
+  assert (dot_index != 0);
+  string host = server.host_name.substr (0, dot_index);
+  string domain = server.host_name.substr (dot_index + 1);
+
+  if (domain == "local")
+    domain = s.get_domain_name ();
+
+  string host_name;
+  if (host + domain == "localhost" + s.get_domain_name () ||
+      host + domain == s.get_host_name () + s.get_domain_name ())
+    host_name = "localhost";
+  else
+    host_name = host + "." + domain;
+
+  // Attempt connection using each of the available client ceritificate
+  // databases.
+  vector<string> dbs = private_ssl_dbs;
+  vector<string>::iterator i = dbs.end();
+  dbs.insert (i, public_ssl_dbs.begin (), public_ssl_dbs.end ());
+  for (i = dbs.begin (); i != dbs.end (); ++i)
+    {
+      const char *cert_dir = i->c_str ();
+
+      if (s.verbose > 1)
+	clog << "Attempting connection with " << server
+	     << " (" << host_name << ")" << endl
+	     << "  using certificates from the database in " << cert_dir
+	     << endl;
+      // Call the NSPR initialization routines.
+      PR_Init (PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
+
+#if 0 // no client authentication for now.
+      // Set our password function callback.
+      PK11_SetPasswordFunc (myPasswd);
+#endif
+
+      // Initialize the NSS libraries.
+      SECStatus secStatus = NSS_InitReadWrite (cert_dir);
+      if (secStatus != SECSuccess)
+	{
+	  // Try it again, readonly.
+	  secStatus = NSS_Init(cert_dir);
+	  if (secStatus != SECSuccess)
+	    {
+	      cerr << "Error initializing NSS" << endl;
+	      goto error;
+	    }
+	}
+
+      // All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy.
+      NSS_SetDomesticPolicy ();
+
+      server_zipfile = s.tmpdir + "/server.zip";
+      int rc = client_main (host_name.c_str (), server.port,
+			    client_zipfile.c_str(), server_zipfile.c_str ());
+
+      NSS_Shutdown();
+      PR_Cleanup();
+
+      if (rc == 0)
+	return rc; // Success!
+    }
+
+  return 1; // Failure
+
+ error:
+  nssError ();
+  NSS_Shutdown();
+  PR_Cleanup();
+#endif // HAVE_NSS
+
+  return 1; // Failure
+}
+
+int
+compile_server_client::unpack_response ()
+{
+  // Unzip the response package.
+  server_tmpdir = s.tmpdir + "/server";
+  string cmd = "unzip -qd " + server_tmpdir + " " + server_zipfile;
+  int rc = stap_system (s.verbose, cmd);
+  if (rc != 0)
+    {
+      cerr << "Unable to unzip the server reponse '" << server_zipfile << '\''
+	   << endl;
+    }
+
+  // If the server's response contains a systemtap temp directory, move
+  // its contents to our temp directory.
+  glob_t globbuf;
+  string filespec = server_tmpdir + "/stap??????";
+  if (s.verbose > 1)
+    clog << "Searching \"" << filespec << "\"" << endl;
+  int r = glob(filespec.c_str (), 0, NULL, & globbuf);
+  if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
+    {
+      if (globbuf.gl_pathc > 1)
+	{
+	  cerr << "Incorrect number of files in server response" << endl;
+	  rc = 1; goto done;
+	}
+
+      assert (globbuf.gl_pathc == 1);
+      string dirname = globbuf.gl_pathv[0];
+      if (s.verbose > 1)
+	clog << "  found " << dirname << endl;
+
+      filespec = dirname + "/*";
+      if (s.verbose > 1)
+	clog << "Searching \"" << filespec << "\"" << endl;
+      int r = glob(filespec.c_str (), GLOB_PERIOD, NULL, & globbuf);
+      if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
+	{
+	  unsigned prefix_len = dirname.size () + 1;
+	  for (unsigned i = 0; i < globbuf.gl_pathc; ++i)
+	    {
+	      string oldname = globbuf.gl_pathv[i];
+	      if (oldname.substr (oldname.size () - 2) == "/." ||
+		  oldname.substr (oldname.size () - 3) == "/..")
+		continue;
+	      string newname = s.tmpdir + "/" + oldname.substr (prefix_len);
+	      if (s.verbose > 1)
+		clog << "  found " << oldname
+		     << " -- linking from " << newname << endl;
+	      rc = symlink (oldname.c_str (), newname.c_str ());
+	      if (rc != 0)
+		{
+		  cerr << "Unable to link '" << oldname
+		       << "' to '" << newname << "': "
+		       << strerror (errno) << endl;
+		  goto done;
+		}
+	    }
+	}
+    }
+
+  // Remove the output line due to the synthetic server-side -k
+  cmd = "sed -i '/^Keeping temporary directory.*/ d' " +
+    server_tmpdir + "/stderr";
+  stap_system (s.verbose, cmd);
+
+  // Remove the output line due to the synthetic server-side -p4
+  cmd = "sed -i '/^.*\\.ko$/ d' " + server_tmpdir + "/stdout";
+  stap_system (s.verbose, cmd);
+
+ done:
+  globfree (& globbuf);
+  return rc;
+}
+
+int
+compile_server_client::process_response ()
+{
+  // Pick up the results of running stap on the server.
+  string filename = server_tmpdir + "/rc";
+  int stap_rc;
+  int rc = read_from_file (filename, stap_rc);
+  if (rc != 0)
+    return rc;
+  rc = stap_rc;
+
+  if (s.last_pass >= 4)
+    {
+      // The server should have returned a module.
+      string filespec = s.tmpdir + "/*.ko";
+      if (s.verbose > 1)
+	clog << "Searching \"" << filespec << "\"" << endl;
+
+      glob_t globbuf;
+      int r = glob(filespec.c_str (), 0, NULL, & globbuf);
+      if (r != GLOB_NOSPACE && r != GLOB_ABORTED && r != GLOB_NOMATCH)
+	{
+	  if (globbuf.gl_pathc > 1)
+	    cerr << "Incorrect number of modules in server response" << endl;
+	  else
+	    {
+	      assert (globbuf.gl_pathc == 1);
+	      string modname = globbuf.gl_pathv[0];
+	      if (s.verbose > 1)
+		clog << "  found " << modname << endl;
+
+	      // If a module name was not specified by the user, then set it to
+	      // be the one generated by the server.
+	      if (! s.save_module)
+		{
+		  vector<string> components;
+		  tokenize (modname, components, "/");
+		  s.module_name = components.back ();
+		  s.module_name.erase(s.module_name.size() - 3);
+		}
+	    }
+	}
+      else if (s.have_script)
+	{
+	  if (rc == 0)
+	    {
+	      cerr << "No module was returned by the server" << endl;
+	      rc = 1;
+	    }
+	}
+      globfree (& globbuf);
+    }
+
+  // Output stdout and stderr.
+  filename = server_tmpdir + "/stderr";
+  flush_to_stream (filename, cerr);
+
+  filename = server_tmpdir + "/stdout";
+  flush_to_stream (filename, cout);
+
+  return rc;
+}
+
+int
+compile_server_client::read_from_file (const string &fname, int &data)
+{
+  // We don't use C++ streams here because they provide no useful information
+  // in the event of a failure.
+  FILE *f = fopen (fname.c_str (), "r");
+  if (! f)
+    {
+      cerr << "Unable to open file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      return 1;
+    }
+
+  int rc = 0;
+  if (! fscanf (f, "%d", & data))
+    {
+      cerr << "Unable to read from file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      rc = 1;
+    }
+
+  fclose (f);
+  return rc;
+}
+
+int
+compile_server_client::write_to_file (const string &fname, const string &data)
+{
+  // We don't use C++ streams here because they provide no useful information
+  // in the event of a failure.
+  FILE *f = fopen (fname.c_str (), "w");
+  if (! f)
+    {
+      cerr << "Unable to open file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      return 1;
+    }
+
+  int rc = 0;
+  if (! fwrite (data.c_str (), 1, data.size(), f))
+    {
+      cerr << "Unable to write to file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      rc = 1;
+    }
+
+  fclose (f);
+  return rc;
+}
+
+int
+compile_server_client::flush_to_stream (const string &fname, ostream &o)
+{
+  // We don't use C++ streams here because they provide no useful information
+  // in the event of a failure.
+  FILE *f = fopen (fname.c_str (), "r");
+  if (! f)
+    {
+      cerr << "Unable to open file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      return 1;
+    }
+
+  // Copy the data.
+  char buf[256];
+  while (fgets (buf, sizeof (buf), f))
+    o << buf;
+
+  int rc = 0;
+  if (! feof (f))
+    {
+      cerr << "Error reading file '" << fname << "': "
+	   << strerror (errno)
+	   << endl;
+      rc = 1;
+    }
+
+  fclose (f);
+  return rc;
+}
+// Utility Functions.
+std::ostream &operator<< (std::ostream &s, const compile_server_info &i)
+{
+  return s << i.host_name << ' '
+	   << i.ip_address << ' '
+	   << i.port << ' '
+	   << i.sysinfo;
+}
+
+void
+query_server_status (const systemtap_session &s)
+{
+  unsigned limit = s.server_status_strings.size ();
+  for (unsigned i = 0; i < limit; ++i)
+    query_server_status (s, s.server_status_strings[i]);
+}
+
+void
+query_server_status (const systemtap_session &s, const string &status_string)
+{
+#if HAVE_NSS || HAVE_AVAHI
+  // If this string is empty, then set the default.
+  // If the --server option has been used
+  //   the default is 'specified'
+  // otherwise if the --unprivileged has been used
+  //   the default is online,trusted,compatible,signer
+  // otherwise
+  //   the default is online,trusted,compatible
+  //
+  // Having said that,
+  //   'online' is only applicable if we have avahi
+  //   'trusted' and 'signer' are only applicable if we have NSS
+  string working_string;
+  if (status_string.empty ())
+    {
+      if (s.specified_servers.empty ())
+	{
+#if HAVE_AVAHI
+	  working_string = "online,";
+#endif
+#if HAVE_NSS
+	  working_string += "trusted,";
+	  if (s.unprivileged)
+	    working_string += "signer,";
+#endif
+	  working_string += "compatible";
+	}
+      else
+	working_string = "specified";
+    }
+  else
+    working_string = status_string;
+
+  clog << "Systemtap Compile Server Status for '" << working_string << '\''
+       << endl;
+
+  // Construct a mask of the server properties that have been requested.
+  // The available properties are:
+  //     trusted    - trusted servers only.
+  //	 online     - online servers only.
+  //     compatible - servers which compile for the current kernel release
+  //	 	      and architecture.
+  //     signer     - servers which are trusted module signers.
+  //	 specified  - servers which have been specified using --server=XXX.
+  //	 	      If no servers have been specified, then this is
+  //		      equivalent to --server-status=trusted,online,compatible.
+  //     all        - all trusted servers, servers currently online and
+  //	              specified servers.
+  vector<string> properties;
+  tokenize (working_string, properties, ",");
+  int pmask = 0;
+  unsigned limit = properties.size ();
+  for (unsigned i = 0; i < limit; ++i)
+    {
+      const string &property = properties[i];
+      if (property == "all")
+	{
+	  pmask |= compile_server_specified;
+#if HAVE_NSS
+	  pmask |= compile_server_trusted;
+#endif
+#if HAVE_AVAHI
+	  pmask |= compile_server_online;
+#endif
+	}
+      else if (property == "specified")
+	{
+	  pmask |= compile_server_specified;
+	}
+#if HAVE_NSS
+      else if (property == "trusted")
+	{
+	  pmask |= compile_server_trusted;
+	}
+#endif
+#if HAVE_AVAHI
+      else if (property == "online")
+	{
+	  pmask |= compile_server_online;
+	}
+#endif
+      else if (property == "compatible")
+	{
+	  pmask |= compile_server_compatible;
+	}
+#if HAVE_NSS
+      else if (property == "signer")
+	{
+	  pmask |= compile_server_signer;
+	}
+#endif
+      else
+	{
+	  cerr << "Warning: unsupported compile server property: " << property
+	       << endl;
+	}
+    }
+
+  // Now obtain a list of the servers which match the criteria.
+  vector<compile_server_info> servers;
+  get_server_info (s, pmask, servers);
+
+  // Print the server information
+  limit = servers.size ();
+  for (unsigned i = 0; i < limit; ++i)
+    {
+      clog << servers[i] << endl;
+    }
+#endif // HAVE_NSS || HAVE_AVAHI
+}
+
+void
+get_server_info (
+  const systemtap_session &s,
+  int pmask,
+  vector<compile_server_info> &servers
+)
+{
+  // Get information on compile servers matching the requested criteria.
+  // XXXX: TODO: Only compile_server_online and compile_server_compatible
+  //             are currently implemented.
+  if ((pmask & compile_server_online))
+    {
+      get_online_server_info (servers);
+    }
+  if ((pmask & compile_server_compatible))
+    {
+      keep_compatible_server_info (s, servers);
+    }
+}
+
+void
+keep_compatible_server_info (
+  const systemtap_session &s,
+  vector<compile_server_info> &servers
+)
+{
+  // Remove entries for servers incompatible with the host environment
+  // from the given list of servers.
+  // A compatible server compiles for the kernel release and architecture
+  // of the host environment.
+  for (unsigned i = 0; i < servers.size (); /**/)
+    {
+      // Extract the sysinfo field from the Avahi TXT.
+      string sysinfo = servers[i].sysinfo;
+      size_t ix = sysinfo.find("\"sysinfo=");
+      if (ix == string::npos)
+	{
+	  // No sysinfo field. Treat as incompatible.
+	  servers.erase (servers.begin () + i);
+	  continue;
+	}
+      sysinfo = sysinfo.substr (ix + sizeof ("\"sysinfo=") - 1);
+      ix = sysinfo.find('"');
+      if (ix == string::npos)
+	{
+	  // No end of sysinfo field. Treat as incompatible.
+	  servers.erase (servers.begin () + i);
+	  continue;
+	}
+      sysinfo = sysinfo.substr (0, ix);
+
+      // Break the sysinfo into kernel release and arch.
+      vector<string> release_arch;
+      tokenize (sysinfo, release_arch, " ");
+      if (release_arch.size () != 2)
+	{
+	  // Bad sysinfo data. Treat as incompatible.
+	  servers.erase (servers.begin () + i);
+	  continue;
+	}
+      if (release_arch[0] != s.kernel_release ||
+	  release_arch[1] != s.architecture)
+	{
+	  // Platform mismatch.
+	  servers.erase (servers.begin () + i);
+	  continue;
+	}
+  
+      // The server is compatible. Leave it in the list.
+      ++i;
+    }
+}
+
+#if HAVE_AVAHI
+struct browsing_context {
+  AvahiSimplePoll *simple_poll;
+  AvahiClient *client;
+  vector<compile_server_info> *servers;
+};
+
+extern "C"
+void resolve_callback(
+    AvahiServiceResolver *r,
+    AVAHI_GCC_UNUSED AvahiIfIndex interface,
+    AVAHI_GCC_UNUSED AvahiProtocol protocol,
+    AvahiResolverEvent event,
+    const char *name,
+    const char *type,
+    const char *domain,
+    const char *host_name,
+    const AvahiAddress *address,
+    uint16_t port,
+    AvahiStringList *txt,
+    AvahiLookupResultFlags flags,
+    AVAHI_GCC_UNUSED void* userdata) {
+
+    assert(r);
+    const browsing_context *context = (browsing_context *)userdata;
+    vector<compile_server_info> *servers = context->servers;
+
+    // Called whenever a service has been resolved successfully or timed out.
+
+    switch (event) {
+        case AVAHI_RESOLVER_FAILURE:
+	  cerr << "Failed to resolve service '" << name
+	       << "' of type '" << type
+	       << "' in domain '" << domain
+	       << "': " << avahi_strerror(avahi_client_errno(avahi_service_resolver_get_client(r)))
+	       << endl;
+            break;
+
+        case AVAHI_RESOLVER_FOUND: {
+            char a[AVAHI_ADDRESS_STR_MAX], *t;
+            avahi_address_snprint(a, sizeof(a), address);
+            t = avahi_string_list_to_string(txt);
+
+	    // Save the information of interest.
+	    compile_server_info info;
+	    info.ip_address = strdup (a);
+	    info.port = port;
+	    info.sysinfo = t;
+	    info.host_name = host_name;
+
+	    // Add this server to the list of discovered servers.
+	    servers->push_back (info);
+
+            avahi_free(t);
+        }
+    }
+
+    avahi_service_resolver_free(r);
+}
+
+extern "C"
+void browse_callback(
+    AvahiServiceBrowser *b,
+    AvahiIfIndex interface,
+    AvahiProtocol protocol,
+    AvahiBrowserEvent event,
+    const char *name,
+    const char *type,
+    const char *domain,
+    AVAHI_GCC_UNUSED AvahiLookupResultFlags flags,
+    void* userdata) {
+    
+    browsing_context *context = (browsing_context *)userdata;
+    AvahiClient *c = context->client;
+    AvahiSimplePoll *simple_poll = context->simple_poll;
+    assert(b);
+
+    // Called whenever a new services becomes available on the LAN or is removed from the LAN.
+
+    switch (event) {
+        case AVAHI_BROWSER_FAILURE:
+	    cerr << "Avahi browse failed: "
+		 << avahi_strerror(avahi_client_errno(avahi_service_browser_get_client(b)))
+		 << endl;
+	    avahi_simple_poll_quit(simple_poll);
+	    break;
+
+        case AVAHI_BROWSER_NEW:
+	    // We ignore the returned resolver object. In the callback
+	    // function we free it. If the server is terminated before
+	    // the callback function is called the server will free
+	    // the resolver for us.
+            if (!(avahi_service_resolver_new(c, interface, protocol, name, type, domain,
+					     AVAHI_PROTO_UNSPEC, (AvahiLookupFlags)0, resolve_callback, context))) {
+	      cerr << "Failed to resolve service '" << name
+		   << "': " << avahi_strerror(avahi_client_errno(c))
+		   << endl;
+	    }
+            break;
+
+        case AVAHI_BROWSER_REMOVE:
+        case AVAHI_BROWSER_ALL_FOR_NOW:
+        case AVAHI_BROWSER_CACHE_EXHAUSTED:
+            break;
+    }
+}
+
+extern "C"
+void client_callback(AvahiClient *c, AvahiClientState state, AVAHI_GCC_UNUSED void * userdata) {
+    assert(c);
+    browsing_context *context = (browsing_context *)userdata;
+    AvahiSimplePoll *simple_poll = context->simple_poll;
+
+    // Called whenever the client or server state changes.
+
+    if (state == AVAHI_CLIENT_FAILURE) {
+        cerr << "Avahi Server connection failure: "
+	     << avahi_strerror(avahi_client_errno(c))
+	     << endl;
+        avahi_simple_poll_quit(simple_poll);
+    }
+}
+
+extern "C"
+void timeout_callback(AVAHI_GCC_UNUSED AvahiTimeout *e, AVAHI_GCC_UNUSED void *userdata) {
+  browsing_context *context = (browsing_context *)userdata;
+  AvahiSimplePoll *simple_poll = context->simple_poll;
+  avahi_simple_poll_quit(simple_poll);
+}
+#endif // HAVE_AVAHI
+
+void
+get_online_server_info (vector<compile_server_info> &servers)
+{
+#if HAVE_AVAHI
+    // Initialize.
+    AvahiClient *client = NULL;
+    AvahiServiceBrowser *sb = NULL;
+ 
+    // Allocate main loop object.
+    AvahiSimplePoll *simple_poll;
+    if (!(simple_poll = avahi_simple_poll_new())) {
+        cerr << "Failed to create Avahi simple poll object" << endl;
+        goto fail;
+    }
+    browsing_context context;
+    context.simple_poll = simple_poll;
+    context.servers = & servers;
+
+    // Allocate a new Avahi client
+    int error;
+    client = avahi_client_new (avahi_simple_poll_get (simple_poll),
+			       (AvahiClientFlags)0,
+			       client_callback, & context, & error);
+
+    // Check whether creating the client object succeeded.
+    if (!client) {
+        cerr << "Failed to create Avahi client: "
+	     << avahi_strerror(error)
+	     << endl;
+        goto fail;
+    }
+    context.client = client;
+    
+    // Create the service browser.
+    if (!(sb = avahi_service_browser_new (client, AVAHI_IF_UNSPEC,
+					  AVAHI_PROTO_UNSPEC, "_stap._tcp",
+					  NULL, (AvahiLookupFlags)0,
+					  browse_callback, & context))) {
+        cerr << "Failed to create Avahi service browser: "
+	     << avahi_strerror(avahi_client_errno(client))
+	     << endl;
+        goto fail;
+    }
+
+    // Timeout after 2 seconds.
+    struct timeval tv;
+    avahi_simple_poll_get(simple_poll)->timeout_new(
+        avahi_simple_poll_get(simple_poll),
+        avahi_elapse_time(&tv, 1000*2, 0),
+        timeout_callback,
+        & context);
+
+    // Run the main loop.
+    avahi_simple_poll_loop(simple_poll);
+    
+fail:
+    // Cleanup.
+    if (sb)
+        avahi_service_browser_free(sb);
+    
+    if (client)
+        avahi_client_free(client);
+
+    if (simple_poll)
+        avahi_simple_poll_free(simple_poll);
+#endif // HAVE_AVAHI
+}
diff --git a/csclient.h b/csclient.h
new file mode 100644
index 0000000..1f3b61f
--- /dev/null
+++ b/csclient.h
@@ -0,0 +1,76 @@
+// -*- C++ -*-
+// Copyright (C) 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.
+#ifndef CSCLIENT_H
+#define CSCLIENT_H
+
+// Information about compile servers.
+struct compile_server_info
+{
+  std::string host_name;
+  std::string ip_address;
+  unsigned short port;
+  std::string sysinfo;
+};
+
+enum compile_server_properties {
+  compile_server_trusted    = 0x1,
+  compile_server_online     = 0x2,
+  compile_server_compatible = 0x4,
+  compile_server_signer     = 0x8,
+  compile_server_specified  = 0x10
+};
+
+class compile_server_client
+{
+public:
+  compile_server_client (systemtap_session &s) : s(s) {}
+  int passes_0_4 ();
+
+private:
+  // Client/server session methods.
+  int initialize ();
+  int create_request ();
+  int package_request ();
+  int find_and_connect_to_server ();
+  int unpack_response ();
+  int process_response ();
+
+  // Client/server utility methods.
+  int include_file_or_directory (
+    const std::string &subdir,
+    const std::string &path,
+    const char *option = 0
+  );
+  int add_package_args ();
+  int add_package_arg (const std::string &arg);
+  int compile_using_server (const compile_server_info &server);
+
+  int read_from_file (const std::string &fname, int &data);
+  int write_to_file (const std::string &fname, const std::string &data);
+  int flush_to_stream (const std::string &fname, std::ostream &o);
+
+  systemtap_session &s;
+  std::vector<std::string> private_ssl_dbs;
+  std::vector<std::string> public_ssl_dbs;
+  std::string client_tmpdir;
+  std::string client_zipfile;
+  std::string server_tmpdir;
+  std::string server_zipfile;
+  unsigned argc;
+};
+
+// Utility functions
+void query_server_status (const systemtap_session &s);
+void query_server_status (const systemtap_session &s, const std::string &status_string);
+void get_server_info (const systemtap_session &s, int pmask, std::vector<compile_server_info> &servers);
+void get_online_server_info (std::vector<compile_server_info> &servers);
+void keep_compatible_server_info (const systemtap_session &s, std::vector<compile_server_info> &servers);
+
+std::ostream &operator<< (std::ostream &s, const compile_server_info &i);
+
+#endif // CSCLIENT_H
diff --git a/main.cxx b/main.cxx
index fdcd113..298c5ba 100644
--- a/main.cxx
+++ b/main.cxx
@@ -21,15 +21,11 @@
 #include "coveragedb.h"
 #include "rpm_finder.h"
 #include "task_finder.h"
+#include "csclient.h"
 
 #include "sys/sdt.h"
 
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <cerrno>
 #include <cstdlib>
-#include <limits.h>
 
 extern "C" {
 #include <glob.h>
@@ -361,29 +357,54 @@ int parse_kernel_exports (systemtap_session &s)
 }
 
 
-/*
- * Returns a string describing memory resource usage.
- * Since it seems getrusage() doesn't maintain the mem related fields,
- * this routine parses /proc/self/statm to get the statistics.
- */
-static string
-getmemusage ()
+static void
+create_temp_dir (systemtap_session &s)
 {
-  static long sz = sysconf(_SC_PAGESIZE);
-
-  long pages, kb;
-  ostringstream oss;
-  ifstream statm("/proc/self/statm");
-  statm >> pages;
-  kb = pages * sz / 1024;
-  oss << "using " << kb << "virt/";
-  statm >> pages;
-  kb = pages * sz / 1024;
-  oss << kb << "res/";
-  statm >> pages;
-  kb = pages * sz / 1024;
-  oss << kb << "shr kb, ";
-  return oss.str();
+  // Create a temporary directory to build within.
+  // Be careful with this, as "tmpdir" is "rm -rf"'d at the end.
+  const char* tmpdir_env = getenv("TMPDIR");
+  if (! tmpdir_env)
+    tmpdir_env = "/tmp";
+
+  string stapdir = "/stapXXXXXX";
+  string tmpdirt = tmpdir_env + stapdir;
+  mode_t mask = umask(0);
+  const char *tmpdir_name = mkdtemp((char *)tmpdirt.c_str());
+  umask(mask);
+  if (! tmpdir_name)
+    {
+      const char* e = strerror (errno);
+      cerr << "ERROR: cannot create temporary directory (\"" << tmpdirt << "\"): " << e << endl;
+      exit (1); // die
+    }
+  else
+    s.tmpdir = tmpdir_name;
+
+  if (s.verbose>1)
+    clog << "Created temporary directory \"" << s.tmpdir << "\"" << endl;
+}
+
+static void
+remove_temp_dir (systemtap_session &s)
+{
+  if (s.tmpdir != "")
+    {
+      if (s.keep_tmpdir)
+        // NB: the format of this message needs to match the expectations
+        // of stap-server-connect.c.
+        clog << "Keeping temporary directory \"" << s.tmpdir << "\"" << endl;
+      else
+        {
+	  // Ignore signals while we're deleting the temporary directory.
+	  setup_signals (SIG_IGN);
+
+	  // Remove the temporary directory.
+          string cleanupcmd = "rm -rf ";
+          cleanupcmd += s.tmpdir;
+
+	  (void) stap_system (s.verbose, cleanupcmd);
+        }
+    }
 }
 
 static int
@@ -391,6 +412,17 @@ passes_0_4 (systemtap_session &s)
 {
   int rc = 0;
   
+  // Create a temporary directory to build within.
+  // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end.
+  create_temp_dir (s);
+
+  // Perform passes 0 through 4 using a compile server?
+  if (! s.specified_servers.empty ())
+    {
+      compile_server_client client (s);
+      return client.passes_0_4 ();
+    }
+
   // PASS 0: setting up
   s.verbose = s.perpass_verbose[0];
   STAP_PROBE1(stap, pass0__start, &s);
@@ -413,31 +445,6 @@ passes_0_4 (systemtap_session &s)
            << endl;
     }
 
-  // Create a temporary directory to build within.
-  // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end.
-  {
-    const char* tmpdir_env = getenv("TMPDIR");
-    if (! tmpdir_env)
-      tmpdir_env = "/tmp";
-
-    string stapdir = "/stapXXXXXX";
-    string tmpdirt = tmpdir_env + stapdir;
-    mode_t mask = umask(0);
-    const char* tmpdir = mkdtemp((char *)tmpdirt.c_str());
-    umask(mask);
-    if (! tmpdir)
-      {
-        const char* e = strerror (errno);
-        cerr << "ERROR: cannot create temporary directory (\"" << tmpdirt << "\"): " << e << endl;
-        exit (1); // die
-      }
-    else
-      s.tmpdir = tmpdir;
-
-    if (s.verbose>1)
-      clog << "Created temporary directory \"" << s.tmpdir << "\"" << endl;
-  }
-
   // Now that no further changes to s.kernel_build_tree can occur, let's use it.
   if (parse_kernel_config (s) != 0)
     exit (1);
@@ -822,26 +829,7 @@ cleanup (systemtap_session &s, int rc)
   }
 
   // Clean up temporary directory.  Obviously, be careful with this.
-  if (s.tmpdir == "")
-    ; // do nothing
-  else
-    {
-      if (s.keep_tmpdir)
-        // NB: the format of this message needs to match the expectations
-        // of stap-server-connect.c.
-        clog << "Keeping temporary directory \"" << s.tmpdir << "\"" << endl;
-      else
-        {
-	  // Ignore signals while we're deleting the temporary directory.
-	  setup_signals (SIG_IGN);
-
-	  // Remove the temporary directory.
-          string cleanupcmd = "rm -rf ";
-          cleanupcmd += s.tmpdir;
-
-	  (void) stap_system (s.verbose, cleanupcmd);
-        }
-    }
+  remove_temp_dir (s);
 
   STAP_PROBE1(stap, pass6__end, &s);
 }
@@ -851,22 +839,29 @@ main (int argc, char * const argv [])
 {
   // Initialize defaults.
   systemtap_session s;
-  s.initialize ();
 
   // Process the command line.
   int rc = s.parse_cmdline (argc, argv);
   if (rc != 0)
     exit (rc);
 
-  // Check for options conflicts.
+  // Check for options conflicts. Exits if errors are detected.
   s.check_options (argc, argv);
 
-  // Run passes 0-4
-  rc = passes_0_4 (s);
+  // If requested, query server status. This is independent of other tasks.
+  query_server_status (s);
 
-  // Run pass 5, if requested
-  if (rc == 0 && s.last_pass >= 5 && ! pending_interrupts)
-    rc = pass_5 (s);
+  // Run the passes only if a script has been specified. The requirement for
+  // a script has already been checked in systemtap_session::check_options.
+  if (s.have_script)
+    {
+      // Run passes 0-4, either locally or using a compile server.
+      rc = passes_0_4 (s);
+
+      // Run pass 5, if requested
+      if (rc == 0 && s.last_pass >= 5 && ! pending_interrupts)
+	rc = pass_5 (s);
+    }
 
   // Pass 6. Cleanup
   cleanup (s, rc);
@@ -875,4 +870,3 @@ main (int argc, char * const argv [])
 }
 
 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
-
diff --git a/session.cxx b/session.cxx
index 8613988..dae08e4 100644
--- a/session.cxx
+++ b/session.cxx
@@ -10,17 +10,25 @@
 #include "session.h"
 #include "cache.h"
 #include "elaborate.h"
+#include "translate.h"
+#include "buildrun.h"
+#include "coveragedb.h"
 #include "hash.h"
 #include "task_finder.h"
+#include "csclient.h"
+#include "rpm_finder.h"
 #include "util.h"
 #include "git_version.h"
 
+#include "sys/sdt.h"
+
+#include <cerrno>
+#include <cstdlib>
+
 extern "C" {
-#include <errno.h>
 #include <getopt.h>
 #include <limits.h>
-#include <stdlib.h>
-#include <string.h>
+#include <sys/stat.h>
 #include <sys/utsname.h>
 #include <elfutils/libdwfl.h>
 }
@@ -61,12 +69,6 @@ systemtap_session::systemtap_session ():
   module_cache (0),
   last_token (0)
 {
-}
-
-// Initialize to the default settings.
-void
-systemtap_session::initialize()
-{
   struct utsname buf;
   (void) uname (& buf);
   kernel_release = string (buf.release);
@@ -93,8 +95,11 @@ systemtap_session::initialize()
   architecture = machine;
 
   for (unsigned i=0; i<5; i++) perpass_verbose[i]=0;
+  verbose = 0;
 
   have_script = false;
+  runtime_specified = false;
+  include_arg_start = -1;
   timing = false;
   guru_mode = false;
   bulk_mode = false;
@@ -137,19 +142,12 @@ systemtap_session::initialize()
   unwindsym_ldd = false;
   client_options = false;
 
-  // Location of our signing certificate.
-  // If we're root, use the database in SYSCONFDIR, otherwise
-  // use the one in our $HOME directory.  */
-  if (getuid() == 0)
-    cert_db_path = SYSCONFDIR "/systemtap/ssl/server";
-  else
-    cert_db_path = getenv("HOME") + string ("/.systemtap/ssl/server");
   /*  adding in the XDG_DATA_DIRS variable path,
- *  this searches in conjunction with SYSTEMTAP_TAPSET
- *  to locate stap scripts, either can be disabled if 
- *  needed using env $PATH=/dev/null where $PATH is the 
- *  path you want disabled
- */  
+   *  this searches in conjunction with SYSTEMTAP_TAPSET
+   *  to locate stap scripts, either can be disabled if 
+   *  needed using env $PATH=/dev/null where $PATH is the 
+   *  path you want disabled
+   */  
   const char* s_p1 = getenv ("XDG_DATA_DIRS");
   if ( s_p1 != NULL )
   {
@@ -206,14 +204,6 @@ systemtap_session::initialize()
 	}
     }
 
-  // Location of our signing certificate.
-  // If we're root, use the database in SYSCONFDIR, otherwise
-  // use the one in data_path.  */
-  if (geteuid() == 0)
-    cert_db_path = SYSCONFDIR "/systemtap/ssl/server";
-  else
-    cert_db_path = data_path + "/ssl/server";
-
   const char* s_tc = getenv ("SYSTEMTAP_COVERAGE");
   if (s_tc != NULL)
     tapset_compile_coverage = true;
@@ -331,6 +321,14 @@ systemtap_session::usage (int exitcode)
     << "              instead of " << compatible << endl
     << "   --skip-badvars" << endl
     << "              substitute zero for bad context $variables" << endl
+#if HAVE_NSS
+    << "   --server[=SERVER-SPEC]" << endl
+    << "              compile using a systemtap compile server" << endl
+#endif
+#if HAVE_NSS || HAVE_AVAHI
+    << "   --server-status[=PROPERTIES]" << endl
+    << "              report on the status of the specified compile servers" << endl
+#endif
     << endl
     ;
 
@@ -369,7 +367,9 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 #define LONG_OPT_CLEAN_CACHE 13
 #define LONG_OPT_COMPATIBLE 14
 #define LONG_OPT_LDD 15
-#define LONG_OPT_ALL_MODULES 16
+#define LONG_OPT_SERVER 16
+#define LONG_OPT_SERVER_STATUS 17
+#define LONG_OPT_ALL_MODULES 18
       // NB: also see find_hash(), usage(), switch stmt below, stap.1 man page
       static struct option long_options[] = {
         { "kelf", 0, &long_opt, LONG_OPT_KELF },
@@ -393,36 +393,45 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
         { "clean-cache", 0, &long_opt, LONG_OPT_CLEAN_CACHE },
         { "compatible", 1, &long_opt, LONG_OPT_COMPATIBLE },
         { "ldd", 0, &long_opt, LONG_OPT_LDD },
+        { "server", 2, &long_opt, LONG_OPT_SERVER },
+        { "server-status", 2, &long_opt, LONG_OPT_SERVER_STATUS },
         { "all-modules", 0, &long_opt, LONG_OPT_ALL_MODULES },
         { NULL, 0, NULL, 0 }
       };
       int grc = getopt_long (argc, argv, "hVvtp:I:e:o:R:r:a:m:kgPc:x:D:bs:uqwl:d:L:FS:B:W",
-                             long_options, NULL);
+			     long_options, NULL);
       // NB: when adding new options, consider very carefully whether they
       // should be restricted from stap-clients (after --client-options)!
 
       if (grc < 0)
         break;
+      bool push_server_opt = false;
       switch (grc)
         {
         case 'V':
+	  push_server_opt = true;
           version ();
           exit (0);
 
         case 'v':
+	  push_server_opt = true;
           for (unsigned i=0; i<5; i++)
             perpass_verbose[i] ++;
+	  verbose ++;
 	  break;
 
         case 't':
+	  push_server_opt = true;
 	  timing = true;
 	  break;
 
         case 'w':
+	  push_server_opt = true;
 	  suppress_warnings = true;
 	  break;
 
         case 'W':
+	  push_server_opt = true;
 	  panic_warnings = true;
 	  break;
 
@@ -438,15 +447,19 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
               cerr << "Listing (-l) mode implies pass 2." << endl;
               return 1;
             }
+	  push_server_opt = true;
           break;
 
         case 'I':
 	  if (client_options)
 	    client_options_disallowed += client_options_disallowed.empty () ? "-I" : ", -I";
+	  if (include_arg_start == -1)
+	    include_arg_start = include_path.size ();
           include_path.push_back (string (optarg));
           break;
 
         case 'd':
+	  push_server_opt = true;
           {
             // At runtime user module names are resolved through their
             // canonical (absolute) path.
@@ -468,17 +481,20 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 		   << endl;
               return 1;
 	    }
+	  push_server_opt = true;
           cmdline_script = string (optarg);
           have_script = true;
           break;
 
         case 'o':
           // NB: client_options not a problem, since pass 1-4 does not use output_file.
+	  push_server_opt = true;
           output_file = string (optarg);
           break;
 
         case 'R':
           if (client_options) { cerr << "ERROR: -R invalid with --client-options" << endl; return 1; }
+	  runtime_specified = true;
           runtime_path = string (optarg);
           break;
 
@@ -525,38 +541,46 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 	      }
 	  }
 
+	  push_server_opt = true;
 	  use_script_cache = false;
           break;
 
         case 'r':
           if (client_options) // NB: no paths!
             assert_regexp_match("-r parameter from client", optarg, "^[a-z0-9_.-]+$");
+	  push_server_opt = true;
           setup_kernel_release(optarg);
           break;
 
         case 'a':
           assert_regexp_match("-a parameter", optarg, "^[a-z0-9_-]+$");
+	  push_server_opt = true;
           architecture = string(optarg);
           break;
 
         case 'k':
+	  push_server_opt = true;
           keep_tmpdir = true;
           use_script_cache = false; /* User wants to keep a usable build tree. */
           break;
 
         case 'g':
+	  push_server_opt = true;
           guru_mode = true;
           break;
 
         case 'P':
+	  push_server_opt = true;
           prologue_searching = true;
           break;
 
         case 'b':
+	  push_server_opt = true;
           bulk_mode = true;
           break;
 
 	case 'u':
+	  push_server_opt = true;
 	  unoptimized = true;
 	  break;
 
@@ -567,9 +591,11 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
               cerr << "Invalid buffer size (should be 1-4095)." << endl;
 	      return 1;
             }
+	  push_server_opt = true;
           break;
 
 	case 'c':
+	  push_server_opt = true;
 	  cmd = string (optarg);
 	  break;
 
@@ -580,22 +606,26 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 	      cerr << "Invalid target process ID number." << endl;
 	      return 1;
 	    }
+	  push_server_opt = true;
 	  break;
 
 	case 'D':
           assert_regexp_match ("-D parameter", optarg, "^[a-z_][a-z_0-9]*(=-?[a-z_0-9]+)?$");
 	  if (client_options)
 	    client_options_disallowed += client_options_disallowed.empty () ? "-D" : ", -D";
+	  push_server_opt = true;
 	  macros.push_back (string (optarg));
 	  break;
 
 	case 'S':
           assert_regexp_match ("-S parameter", optarg, "^[0-9]+(,[0-9]+)?$");
+	  push_server_opt = true;
 	  size_option = string (optarg);
 	  break;
 
 	case 'q':
           if (client_options) { cerr << "ERROR: -q invalid with --client-options" << endl; return 1; } 
+	  push_server_opt = true;
 	  tapset_compile_coverage = true;
 	  break;
 
@@ -617,16 +647,19 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 		   << endl;
 	      return 1;
             }
+	  push_server_opt = true;
           cmdline_script = string("probe ") + string(optarg) + " {}";
           have_script = true;
           break;
 
         case 'F':
+	  push_server_opt = true;
           load_only = true;
 	  break;
 
 	case 'B':
           if (client_options) { cerr << "ERROR: -B invalid with --client-options" << endl; return 1; } 
+	  push_server_opt = true;
           kbuildflags.push_back (string (optarg));
 	  break;
 
@@ -634,6 +667,7 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
           switch (long_opt)
             {
             case LONG_OPT_KELF:
+	      push_server_opt = true;
 	      consult_symtab = true;
 	      break;
             case LONG_OPT_KMAP:
@@ -643,15 +677,18 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 		  cerr << "You can't specify multiple --kmap options." << endl;
 		  return 1;
 		}
+	      push_server_opt = true;
               if (optarg)
-                kernel_symtab_path = optarg;
+		kernel_symtab_path = optarg;
               else
                 kernel_symtab_path = PATH_TBD;
 	      break;
 	    case LONG_OPT_IGNORE_VMLINUX:
+	      push_server_opt = true;
 	      ignore_vmlinux = true;
 	      break;
 	    case LONG_OPT_IGNORE_DWARF:
+	      push_server_opt = true;
 	      ignore_dwarf = true;
 	      break;
 	    case LONG_OPT_VERBOSE_PASS:
@@ -672,22 +709,42 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
                     return 1;
                   }
                 // NB: we don't do this: last_pass = strlen(optarg);
+		push_server_opt = true;
                 break;
               }
 	    case LONG_OPT_SKIP_BADVARS:
+	      push_server_opt = true;
 	      skip_badvars = true;
 	      break;
 	    case LONG_OPT_UNPRIVILEGED:
+	      push_server_opt = true;
 	      unprivileged = true;
               /* NB: for server security, it is essential that once this flag is
                  set, no future flag be able to unset it. */
 	      break;
 	    case LONG_OPT_OMIT_WERROR:
+	      push_server_opt = true;
 	      omit_werror = true;
 	      break;
 	    case LONG_OPT_CLIENT_OPTIONS:
 	      client_options = true;
 	      break;
+	    case LONG_OPT_SERVER:
+	      if (client_options)
+		client_options_disallowed += client_options_disallowed.empty () ? "--server" : ", --server";
+	      if (optarg)
+		specified_servers.push_back (optarg);
+	      else
+		specified_servers.push_back ("");
+	      break;
+	    case LONG_OPT_SERVER_STATUS:
+	      if (client_options)
+		client_options_disallowed += client_options_disallowed.empty () ? "--server-status" : ", --server-status";
+	      if (optarg )
+		server_status_strings.push_back (optarg);
+	      else
+		server_status_strings.push_back ("");
+	      break;
 	    case LONG_OPT_HELP:
 	      usage (0);
 	      break;
@@ -716,6 +773,7 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
               exit(0);
 
             case LONG_OPT_COMPATIBLE:
+	      push_server_opt = true;
               compatible = optarg;
               break;
 
@@ -724,6 +782,7 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
                   cerr << "ERROR: --ldd is invalid with --client-options" << endl;
                   return 1;
               }
+	      push_server_opt = true;
               unwindsym_ldd = true;
               break;
 
@@ -748,6 +807,18 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
           return 1;
           break;
         }
+
+      // Pass selected options on to the server, if any.
+      if (push_server_opt && ! specified_servers.empty ())
+	{
+	  if (grc == 0)
+	    server_args.push_back (string ("--") +
+				   long_options[long_opt - 1].name);
+	  else
+	    server_args.push_back (string ("-") + (char)grc);
+	  if (optarg)
+	    server_args.push_back (optarg);
+	}
     }
 
   return 0;
@@ -756,6 +827,30 @@ systemtap_session::parse_cmdline (int argc, char * const argv [])
 void
 systemtap_session::check_options (int argc, char * const argv [])
 {
+#if ! HAVE_NSS
+  if (client_options)
+    cerr << "WARNING: --client-options is not supported by this version of systemtap" << endl;
+  if (! specified_servers.empty ())
+    {
+      cerr << "WARNING: --server is not supported by this version of systemtap" << endl;
+      specified_servers.clear ();
+    }
+#endif
+
+#if ! HAVE_NSS && ! HAVE_AVAHI
+  if (! server_status_strings.empty ())
+    {
+      cerr << "WARNING: --server-status is not supported by this version of systemtap" << endl;
+      server_status_strings.clear ();
+    }
+#endif
+
+  if (runtime_specified && ! specified_servers.empty ())
+    {
+      cerr << "Warning: Ignoring --server due to the use of -R" << endl;
+      specified_servers.clear ();
+    }
+
   if (client_options && last_pass > 4)
     {
       last_pass = 4; /* Quietly downgrade.  Server passed through -p5 naively. */
@@ -811,8 +906,12 @@ systemtap_session::check_options (int argc, char * const argv [])
   // NB: this is also triggered if stap is invoked with no arguments at all
   if (! have_script)
     {
-      cerr << "A script must be specified." << endl;
-      usage(1);
+      // We don't need a script if --server-status was specified
+      if (server_status_strings.empty ())
+	{
+	  cerr << "A script must be specified." << endl;
+	  usage(1);
+	}
     }
 
   // translate path of runtime to absolute path
@@ -826,6 +925,284 @@ systemtap_session::check_options (int argc, char * const argv [])
     }
 }
 
+int
+systemtap_session::parse_kernel_config ()
+{
+  // PR10702: pull config options
+  string kernel_config_file = kernel_build_tree + "/.config";
+  struct stat st;
+  int rc = stat(kernel_config_file.c_str(), &st);
+  if (rc != 0)
+    {
+	clog << "Checking \"" << kernel_config_file << "\" failed: " << strerror(errno) << endl
+	     << "Ensure kernel development headers & makefiles are installed." << endl;
+	return rc;
+    }
+
+  ifstream kcf (kernel_config_file.c_str());
+  string line;
+  while (getline (kcf, line))
+    {
+      if (!startswith(line, "CONFIG_")) continue;
+      size_t off = line.find('=');
+      if (off == string::npos) continue;
+      string key = line.substr(0, off);
+      string value = line.substr(off+1, string::npos);
+      kernel_config[key] = value;
+    }
+  if (verbose > 2)
+    clog << "Parsed kernel \"" << kernel_config_file << "\", number of tuples: " << kernel_config.size() << endl;
+  
+  kcf.close();
+  return 0;
+}
+
+int
+systemtap_session::parse_kernel_exports ()
+{
+  string kernel_exports_file = kernel_build_tree + "/Module.symvers";
+  struct stat st;
+  int rc = stat(kernel_exports_file.c_str(), &st);
+  if (rc != 0)
+    {
+	clog << "Checking \"" << kernel_exports_file << "\" failed: " << strerror(errno) << endl
+	     << "Ensure kernel development headers & makefiles are installed." << endl;
+	return rc;
+    }
+
+  ifstream kef (kernel_exports_file.c_str());
+  string line;
+  while (getline (kef, line))
+    {
+      vector<string> tokens;
+      tokenize (line, tokens, "\t");
+      if (tokens.size() == 4 &&
+          tokens[2] == "vmlinux" &&
+          tokens[3].substr(0,13) == string("EXPORT_SYMBOL"))
+        kernel_exports.insert (tokens[1]);
+    }
+  if (verbose > 2)
+    clog << "Parsed kernel \"" << kernel_exports_file << "\", number of vmlinux exports: " << kernel_exports.size() << endl;
+  
+  kef.close();
+  return 0;
+}
+
+static void
+uniq_list(list<string>& l)
+{
+	list<string> r;
+	set<string> s;
+
+	for (list<string>::iterator i = l.begin(); i != l.end(); ++i) {
+		s.insert(*i);
+	}
+
+	for (list<string>::iterator i = l.begin(); i != l.end(); ++i) {
+		if (s.find(*i) != s.end()) {
+			s.erase(*i);
+			r.push_back(*i);
+		}
+	}
+
+	l.clear();
+	l.assign(r.begin(), r.end());
+}
+
+void
+systemtap_session::printscript(ostream& o)
+{
+  if (listing_mode)
+    {
+      // We go through some heroic measures to produce clean output.
+      // Record the alias and probe pointer as <name, set<derived_probe *> >
+      map<string,set<derived_probe *> > probe_list;
+
+      // Pre-process the probe alias
+      for (unsigned i=0; i<probes.size(); i++)
+        {
+          if (pending_interrupts) return;
+
+          derived_probe* p = probes[i];
+          // NB: p->basest() is not so interesting;
+          // p->almost_basest() doesn't quite work, so ...
+          vector<probe*> chain;
+          p->collect_derivation_chain (chain);
+          probe* second = (chain.size()>1) ? chain[chain.size()-2] : chain[0];
+
+          #if 0  // dump everything about the derivation chain
+          p->printsig(cerr); cerr << endl;
+          cerr << "chain[" << chain.size() << "]:" << endl;
+          for (unsigned j=0; j<chain.size(); j++)
+            {
+              cerr << "  [" << j << "]: " << endl;
+              cerr << "\tlocations[" << chain[j]->locations.size() << "]:" << endl;
+              for (unsigned k=0; k<chain[j]->locations.size(); k++)
+                {
+                  cerr << "\t  [" << k << "]: ";
+                  chain[j]->locations[k]->print(cerr);
+                  cerr << endl;
+                }
+              const probe_alias *a = chain[j]->get_alias();
+              if (a)
+                {
+                  cerr << "\taliases[" << a->alias_names.size() << "]:" << endl;
+                  for (unsigned k=0; k<a->alias_names.size(); k++)
+                    {
+                      cerr << "\t  [" << k << "]: ";
+                      a->alias_names[k]->print(cerr);
+                      cerr << endl;
+                    }
+                }
+            }
+          #endif
+
+          stringstream tmps;
+          const probe_alias *a = second->get_alias();
+          if (a)
+            {
+              assert (a->alias_names.size() >= 1);
+              a->alias_names[0]->print(tmps); // XXX: [0] is arbitrary; perhaps print all
+            }
+          else
+            {
+              assert (second->locations.size() >= 1);
+              second->locations[0]->print(tmps); // XXX: [0] is less arbitrary here, but still ...
+            }
+          string pp = tmps.str();
+
+          // Now duplicate-eliminate.  An alias may have expanded to
+          // several actual derived probe points, but we only want to
+          // print the alias head name once.
+          probe_list[pp].insert(p);
+        }
+
+      // print probe name and variables if there
+      for (map<string, set<derived_probe *> >::iterator it=probe_list.begin(); it!=probe_list.end(); ++it)
+        {
+          o << it->first; // probe name or alias
+
+          // Print the locals and arguments for -L mode only
+          if (listing_mode_vars)
+            {
+              map<string,unsigned> var_count; // format <"name:type",count>
+              map<string,unsigned> arg_count;
+              list<string> var_list;
+              list<string> arg_list;
+              // traverse set<derived_probe *> to collect all locals and arguments
+              for (set<derived_probe *>::iterator ix=it->second.begin(); ix!=it->second.end(); ++ix)
+                {
+                  derived_probe* p = *ix;
+                  // collect available locals of the probe
+                  for (unsigned j=0; j<p->locals.size(); j++)
+                    {
+                      stringstream tmps;
+                      vardecl* v = p->locals[j];
+                      v->printsig (tmps);
+                      var_count[tmps.str()]++;
+		      var_list.push_back(tmps.str());
+                    }
+                  // collect arguments of the probe if there
+                  list<string> arg_set;
+                  p->getargs(arg_set);
+                  for (list<string>::iterator ia=arg_set.begin(); ia!=arg_set.end(); ++ia) {
+                    arg_count[*ia]++;
+                    arg_list.push_back(*ia);
+		  }
+                }
+
+	      uniq_list(arg_list);
+	      uniq_list(var_list);
+
+              // print the set-intersection only
+              for (list<string>::iterator ir=var_list.begin(); ir!=var_list.end(); ++ir)
+                if (var_count.find(*ir)->second == it->second.size()) // print locals
+                  o << " " << *ir;
+              for (list<string>::iterator ir=arg_list.begin(); ir!=arg_list.end(); ++ir)
+                if (arg_count.find(*ir)->second == it->second.size()) // print arguments
+                  o << " " << *ir;
+            }
+          o << endl;
+        }
+    }
+  else
+    {
+      if (embeds.size() > 0)
+        o << "# global embedded code" << endl;
+      for (unsigned i=0; i<embeds.size(); i++)
+        {
+          if (pending_interrupts) return;
+          embeddedcode* ec = embeds[i];
+          ec->print (o);
+          o << endl;
+        }
+
+      if (globals.size() > 0)
+        o << "# globals" << endl;
+      for (unsigned i=0; i<globals.size(); i++)
+        {
+          if (pending_interrupts) return;
+          vardecl* v = globals[i];
+          v->printsig (o);
+          if (verbose && v->init)
+            {
+              o << " = ";
+              v->init->print(o);
+            }
+          o << endl;
+        }
+
+      if (functions.size() > 0)
+        o << "# functions" << endl;
+      for (map<string,functiondecl*>::iterator it = functions.begin(); it != functions.end(); it++)
+        {
+          if (pending_interrupts) return;
+          functiondecl* f = it->second;
+          f->printsig (o);
+          o << endl;
+          if (f->locals.size() > 0)
+            o << "  # locals" << endl;
+          for (unsigned j=0; j<f->locals.size(); j++)
+            {
+              vardecl* v = f->locals[j];
+              o << "  ";
+              v->printsig (o);
+              o << endl;
+            }
+          if (verbose)
+            {
+              f->body->print (o);
+              o << endl;
+            }
+        }
+
+      if (probes.size() > 0)
+        o << "# probes" << endl;
+      for (unsigned i=0; i<probes.size(); i++)
+        {
+          if (pending_interrupts) return;
+          derived_probe* p = probes[i];
+          p->printsig (o);
+          o << endl;
+          if (p->locals.size() > 0)
+            o << "  # locals" << endl;
+          for (unsigned j=0; j<p->locals.size(); j++)
+            {
+              vardecl* v = p->locals[j];
+              o << "  ";
+              v->printsig (o);
+              o << endl;
+            }
+          if (verbose)
+            {
+              p->body->print (o);
+              o << endl;
+            }
+        }
+    }
+}
+
+
 void systemtap_session::insert_loaded_modules()
 {
   char line[1024];
@@ -1048,6 +1425,55 @@ systemtap_session::print_warning (const string& message_str, const token* tok)
     }
 }
 
+string &
+systemtap_session::get_host_name ()
+{
+  if (host_name.empty ())
+    get_host_and_domain_name ();
+  return host_name;
+}
+
+string &
+systemtap_session::get_domain_name ()
+{
+  if (domain_name.empty ())
+    get_host_and_domain_name ();
+  return domain_name;
+}
+
+void
+systemtap_session::get_host_and_domain_name ()
+{
+  // We can't rely on the existence of MAXHOSTNAMELEN, so loop until we get
+  // a buffer big enough.
+  size_t bufSize = 0;
+  char *buf;
+  for (;;)
+    {
+      bufSize += 100;
+      buf = new char[bufSize];
+      int rc = gethostname (buf, bufSize);
+      if (rc == 0)
+	break;
+      assert (errno == ENAMETOOLONG);
+      delete[] buf;
+    }
+  host_name = buf;
+  delete[] buf;
+
+  // Break the returned name up into host and domain.
+  string::size_type dot_index = host_name.find ('.');
+  if (dot_index != string::npos)
+    {
+      domain_name = host_name.substr (dot_index + 1);
+      host_name = host_name.substr (0, dot_index);
+    }
+  else
+    domain_name = "unknown";
+}
+
+// --------------------------------------------------------------------------
+
 /*
 Perngrq sebz fzvyrlgnc.fit, rkcbegrq gb n 1484k1110 fzvyrlgnc.cat,
 gurapr  catgbcnz | cazfpnyr -jvqgu 160 | 
diff --git a/session.h b/session.h
index 8ef4c11..aaaaf13 100644
--- a/session.h
+++ b/session.h
@@ -9,6 +9,7 @@
 #ifndef SESSION_H
 #define SESSION_H
 
+#include <list>
 #include <string>
 #include <vector>
 #include <iostream>
@@ -17,6 +18,7 @@
 #include <set>
 
 extern "C" {
+#include <signal.h>
 #include <elfutils/libdw.h>
 }
 
@@ -51,7 +53,6 @@ struct semantic_error;
 struct module_cache;
 struct update_visitor;
 
-
 // XXX: a generalized form of this descriptor could be associated with
 // a vardecl instead of out here at the systemtap_session level.
 struct statistic_decl
@@ -73,15 +74,15 @@ struct statistic_decl
   }
 };
 
-
 struct systemtap_session
 {
   systemtap_session ();
+
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (session.cxx)
-  // and/or in the initialize method (session.cxx).
-  void initialize ();
+  // to be cleared in the systemtap_session ctor (session.cxx).
   void setup_kernel_release (const char* kstr);
+  int parse_kernel_config ();
+  int parse_kernel_exports ();
   void insert_loaded_modules ();
 
   // command line parsing
@@ -91,11 +92,15 @@ struct systemtap_session
   void check_options (int argc, char * const argv []);
   static const char* morehelp;
 
+  // NB: It is very important for all of the above (and below) fields
+  // to be cleared in the systemtap_session ctor (session.cxx).
+
   // command line args
   std::string script_file; // FILE
   std::string cmdline_script; // -e PROGRAM
   bool have_script;
   std::vector<std::string> include_path;
+  int include_arg_start;
   std::vector<std::string> macros;
   std::vector<std::string> args;
   std::vector<std::string> kbuildflags;
@@ -108,8 +113,8 @@ struct systemtap_session
   std::string machine;
   std::string architecture;
   std::string runtime_path;
+  bool runtime_specified;
   std::string data_path;
-  std::string cert_db_path;
   std::string module_name;
   std::string stapconf_name;
   std::string output_file;
@@ -136,15 +141,29 @@ struct systemtap_session
   bool tapset_compile_coverage;
   bool need_uprobes;
   bool load_only; // flight recorder mode
-  bool client_options;
-  std::string client_options_disallowed;
-  bool unprivileged;
   bool omit_werror;
+  bool unprivileged;
   bool need_vma_tracker;
 
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (elaborate.cxx)
-  // and/or main.cxx(main).
+  // to be cleared in the systemtap_session ctor (session.cxx).
+
+  // Client/server
+  bool client_options;
+  std::string client_options_disallowed;
+  std::vector<std::string> server_status_strings;
+  std::vector<std::string> specified_servers;
+  std::vector<std::string> server_args;
+
+  std::string host_name;
+  std::string domain_name;
+
+  std::string &get_host_name ();
+  std::string &get_domain_name ();
+  void get_host_and_domain_name ();
+
+  // NB: It is very important for all of the above (and below) fields
+  // to be cleared in the systemtap_session ctor (session.cxx).
 
   // Cache data
   bool use_cache;               // control all caching
@@ -165,8 +184,7 @@ struct systemtap_session
   bool skip_badvars;
 
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (elaborate.cxx)
-  // and/or main.cxx(main).
+  // to be cleared in the systemtap_session ctor (session.cxx).
 
   // temporary directory for module builds etc.
   // hazardous - it is "rm -rf"'d at exit
@@ -216,8 +234,7 @@ struct systemtap_session
   procfs_derived_probe_group* procfs_derived_probes;
 
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (elaborate.cxx)
-  // and/or main.cxx(main).
+  // to be cleared in the systemtap_session ctor (session.cxx).
 
   // unparser data
   translator_output* op;
@@ -235,8 +252,7 @@ struct systemtap_session
   struct module_cache* module_cache;
 
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (elaborate.cxx)
-  // and/or main.cxx(main).
+  // to be cleared in the systemtap_session ctor (session.cxx).
 
   std::set<std::string> seen_errors;
   std::set<std::string> seen_warnings;
@@ -250,16 +266,18 @@ struct systemtap_session
   void print_error (const semantic_error& e);
   void print_error_source (std::ostream&, std::string&, const token* tok);
   void print_warning (const std::string& w, const token* tok = 0);
+  void printscript(std::ostream& o);
 
   // NB: It is very important for all of the above (and below) fields
-  // to be cleared in the systemtap_session ctor (elaborate.cxx)
-  // and/or main.cxx(main).
+  // to be cleared in the systemtap_session ctor (session.cxx).
 };
 
 
 // global counter of SIGINT/SIGTERM's received
 extern int pending_interrupts;
 
+int passes_0_4 ();
+
 #endif // SESSION_H
 
 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/stap-client-connect.c b/stap-client-connect.c
index 0cce123..4a53d99 100644
--- a/stap-client-connect.c
+++ b/stap-client-connect.c
@@ -34,17 +34,13 @@
 #include "nsscommon.h"
 
 #define READ_BUFFER_SIZE (60 * 1024)
-static char *hostName = NULL;
-static unsigned short port = 0;
-static const char *infileName = NULL;
-static const char *outfileName = NULL;
-static char *certDir = NULL;
 static const char *trustNewServer_p = NULL;
 
 /* Exit error codes */
 #define GENERAL_ERROR         1
 #define CA_CERT_INVALID_ERROR 2
 
+#if ! STAP /* temporary until stap-client-connect program goes away*/
 static void
 Usage(const char *progName)
 {
@@ -52,6 +48,7 @@ Usage(const char *progName)
 	  progName);
   exit(1);
 }
+#endif
 
 static void
 exitErr(const char* errorStr, int rc)
@@ -105,7 +102,7 @@ done:
    the chance to trust the server anyway and add the certificate to the
    local database.  */
 static SECStatus
-badCertHandler(void *arg, PRFileDesc *sslSocket)
+badCertHandler(void *arg __attribute__ ((unused)), PRFileDesc *sslSocket)
 {
   SECStatus secStatus;
   PRErrorCode errorNumber;
@@ -210,7 +207,9 @@ setupSSLSocket(void)
 
 
 static SECStatus
-handle_connection(PRFileDesc *sslSocket)
+handle_connection(
+  PRFileDesc *sslSocket, const char *infileName, const char *outfileName
+)
 {
 #if DEBUG
   int	      countRead = 0;
@@ -319,7 +318,13 @@ handle_connection(PRFileDesc *sslSocket)
 /* make the connection.
 */
 static SECStatus
-do_connect(PRNetAddr *addr)
+do_connect(
+  PRNetAddr *addr,
+  const char *hostName,
+  unsigned short port __attribute__ ((unused)),
+  const char *infileName,
+  const char *outfileName
+)
 {
   PRFileDesc *sslSocket;
   PRStatus    prStatus;
@@ -381,7 +386,7 @@ do_connect(PRNetAddr *addr)
   if (secStatus != SECSuccess)
     goto done;
 
-  secStatus = handle_connection(sslSocket);
+  secStatus = handle_connection(sslSocket, infileName, outfileName);
   if (secStatus != SECSuccess)
     goto done;
 
@@ -390,8 +395,9 @@ do_connect(PRNetAddr *addr)
   return secStatus;
 }
 
-static void
-client_main(unsigned short port)
+int
+client_main (const char *hostName, unsigned short port,
+	     const char* infileName, const char* outfileName)
 {
   SECStatus   secStatus;
   PRStatus    prStatus;
@@ -416,9 +422,9 @@ client_main(unsigned short port)
      should succeed. However, don't try forever.  */
   for (attempt = 0; attempt < 5; ++attempt)
     {
-      secStatus = do_connect (&addr);
+      secStatus = do_connect (&addr, hostName, port, infileName, outfileName);
       if (secStatus == SECSuccess)
-	return;
+	return 0;
 
       errorNumber = PR_GetError ();
       switch (errorNumber)
@@ -447,6 +453,7 @@ client_main(unsigned short port)
  failed:
   /* Unrecoverable error */
   exitErr("Unable to connect to server", errCode);
+  return 1;
 }
 
 #if 0 /* No client authorization */
@@ -462,10 +469,16 @@ myPasswd(PK11SlotInfo *info, PRBool retry, void *arg)
 }
 #endif
 
+#if ! STAP /* temporary until stap-client-connect program goes away*/
 int
 main(int argc, char **argv)
 {
-  char *       progName = NULL;
+  const char *progName = NULL;
+  const char *certDir = NULL;
+  const char *hostName = NULL;
+  unsigned short port = 0;
+  const char *infileName = NULL;
+  const char *outfileName = NULL;
   SECStatus    secStatus;
   PLOptState  *optstate;
   PLOptStatus  status;
@@ -513,12 +526,13 @@ main(int argc, char **argv)
   /* All cipher suites except RSA_NULL_MD5 are enabled by Domestic Policy. */
   NSS_SetDomesticPolicy();
 
-  client_main(port);
+  client_main (hostName, port, infileName, outfileName);
 
   NSS_Shutdown();
   PR_Cleanup();
 
   return 0;
 }
+#endif /* ! STAP -- temporary */
 
 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
diff --git a/util.cxx b/util.cxx
index 73ba167..9370a8c 100644
--- a/util.cxx
+++ b/util.cxx
@@ -21,6 +21,7 @@
 #include <cerrno>
 #include <map>
 #include <string>
+#include <fstream>
 
 extern "C" {
 #include <fcntl.h>
@@ -222,6 +223,31 @@ in_group_id (gid_t target_gid)
   return false;
 }
 
+/*
+ * Returns a string describing memory resource usage.
+ * Since it seems getrusage() doesn't maintain the mem related fields,
+ * this routine parses /proc/self/statm to get the statistics.
+ */
+string
+getmemusage ()
+{
+  static long sz = sysconf(_SC_PAGESIZE);
+
+  long pages, kb;
+  ostringstream oss;
+  ifstream statm("/proc/self/statm");
+  statm >> pages;
+  kb = pages * sz / 1024;
+  oss << "using " << kb << "virt/";
+  statm >> pages;
+  kb = pages * sz / 1024;
+  oss << kb << "res/";
+  statm >> pages;
+  kb = pages * sz / 1024;
+  oss << kb << "shr kb, ";
+  return oss.str();
+}
+
 void
 tokenize(const string& str, vector<string>& tokens,
 	 const string& delimiters = " ")
diff --git a/util.h b/util.h
index c6cb455..fecbfab 100644
--- a/util.h
+++ b/util.h
@@ -14,6 +14,7 @@ bool copy_file(const std::string& src, const std::string& dest,
 int create_dir(const char *dir);
 int remove_file_or_dir(const char *dir);
 bool in_group_id (gid_t target_gid);
+std::string getmemusage ();
 void tokenize(const std::string& str, std::vector<std::string>& tokens,
 	      const std::string& delimiters);
 std::string find_executable(const std::string& name,

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]