This is the mail archive of the
systemtap@sourceware.org
mailing list for the systemtap project.
Compile Server Merge into stap - Patch for Feedback
- From: Dave Brolley <brolley at redhat dot com>
- To: SystemTAP <systemtap at sources dot redhat dot com>
- Date: Mon, 14 Jun 2010 15:43:23 -0400
- Subject: 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,