#include "task_finder.h"
#include "csclient.h"
#include "remote.h"
+#include "tapsets.h"
+#include "setupdwfl.h"
#include <libintl.h>
#include <locale.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
+#include <wordexp.h>
}
using namespace std;
// Pre-process the probe alias
for (unsigned i=0; i<s.probes.size(); i++)
{
- if (pending_interrupts) return;
+ assert_no_interrupts();
derived_probe* p = s.probes[i];
// NB: p->basest() is not so interesting;
}
stringstream tmps;
- const probe_alias *a = second->get_alias();
+ // XXX PR14297 make this more accurate wrt complex wildcard expansions
+ const probe_point *a = second->get_alias_loc();
if (a)
{
- assert (a->alias_names.size() >= 1);
- a->alias_names[0]->print(tmps); // XXX: [0] is arbitrary; perhaps print all
+ a->print(tmps);
}
else
{
assert (second->locations.size() >= 1);
- second->locations[0]->print(tmps); // XXX: [0] is less arbitrary here, but still ...
+ second->locations[0]->print(tmps); // XXX: choosing only one location is less arbitrary here than in get_alias_loc(), but still ...
}
string pp = tmps.str();
o << _("# global embedded code") << endl;
for (unsigned i=0; i<s.embeds.size(); i++)
{
- if (pending_interrupts) return;
+ assert_no_interrupts();
embeddedcode* ec = s.embeds[i];
ec->print (o);
o << endl;
o << _("# globals") << endl;
for (unsigned i=0; i<s.globals.size(); i++)
{
- if (pending_interrupts) return;
+ assert_no_interrupts();
vardecl* v = s.globals[i];
v->printsig (o);
if (s.verbose && v->init)
o << _("# functions") << endl;
for (map<string,functiondecl*>::iterator it = s.functions.begin(); it != s.functions.end(); it++)
{
- if (pending_interrupts) return;
+ assert_no_interrupts();
functiondecl* f = it->second;
f->printsig (o);
o << endl;
o << _("# probes") << endl;
for (unsigned i=0; i<s.probes.size(); i++)
{
- if (pending_interrupts) return;
+ assert_no_interrupts();
derived_probe* p = s.probes[i];
p->printsig (o);
o << endl;
{
// This might be nice, but we don't know our current verbosity...
// clog << _F("Received signal %d", sig) << endl << flush;
- kill_stap_spawn(sig);
+ kill_stap_spawn(SIGTERM);
+
pending_interrupts ++;
// Absorb the first two signals. This used to be one, but when
// stap is run under sudo, and then interrupted, sudo relays a
sigaction (SIGXCPU, &sa, NULL);
}
-int parse_kernel_config (systemtap_session &s)
-{
- // PR10702: pull config options
- string kernel_config_file = s.kernel_build_tree + "/.config";
- struct stat st;
- int rc = stat(kernel_config_file.c_str(), &st);
- if (rc != 0)
- {
- clog << _F("Checking \"%s\" failed with error: %s",
- kernel_config_file.c_str(), strerror(errno)) << endl;
- find_devel_rpms(s, s.kernel_build_tree.c_str());
- missing_rpm_list_print(s,"-devel");
- 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);
- s.kernel_config[key] = value;
- }
- if (s.verbose > 2)
- clog << _F("Parsed kernel \"%s\", ", kernel_config_file.c_str())
- << _F(ngettext("containing %zu tuple", "containing %zu tuples",
- s.kernel_config.size()), s.kernel_config.size()) << endl;
-
- kcf.close();
- return 0;
-}
-
-
-int parse_kernel_exports (systemtap_session &s)
-{
- string kernel_exports_file = s.kernel_build_tree + "/Module.symvers";
- struct stat st;
- int rc = stat(kernel_exports_file.c_str(), &st);
- if (rc != 0)
- {
- clog << _F("Checking \"%s\" failed with error: %s\nEnsure kernel development headers & makefiles are installed",
- kernel_exports_file.c_str(), strerror(errno)) << 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"))
- s.kernel_exports.insert (tokens[1]);
- }
- if (s.verbose > 2)
- clog << _F(ngettext("Parsed kernel %s, which contained one vmlinux export",
- "Parsed kernel %s, which contained %zu vmlinux exports",
- s.kernel_exports.size()), kernel_exports_file.c_str(),
- s.kernel_exports.size()) << endl;
-
- kef.close();
- return 0;
-}
-
-
-static int
-create_temp_dir (systemtap_session &s)
-{
- if (!s.tmpdir.empty())
- return 0;
-
- // 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);
- //TRANSLATORS: we can't make the directory due to the error
- cerr << _F("ERROR: cannot create temporary directory (\" %s \"): %s", tmpdirt.c_str(), e) << endl;
- return 1;
- }
- else
- s.tmpdir = tmpdir_name;
-
- if (s.verbose>1)
- clog << _F("Created temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
- return 0;
-}
-
-static void
-remove_temp_dir (systemtap_session &s)
-{
- if (!s.tmpdir.empty())
- {
- if (s.keep_tmpdir)
- // NB: the format of this message needs to match the expectations
- // of stap-serverd.cxx.
- clog << _F("Keeping temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
- else
- {
- // Mask signals while we're deleting the temporary directory.
- stap_sigmasker masked;
-
- // Remove the temporary directory.
- vector<string> cleanupcmd;
- cleanupcmd.push_back("rm");
- cleanupcmd.push_back("-rf");
- cleanupcmd.push_back(s.tmpdir);
-
- (void) stap_system (s.verbose, cleanupcmd);
- s.tmpdir.clear();
- }
- }
-}
// Compilation passes 0 through 4
static int
return 1;
}
- // Create a temporary directory to build within.
- // Be careful with this, as "s.tmpdir" is "rm -rf"'d at the end.
- rc = create_temp_dir (s);
- if (rc)
- return rc;
-
// Perform passes 0 through 4 using a compile server?
if (! s.specified_servers.empty ())
{
#if HAVE_NSS
compile_server_client client (s);
- return client.passes_0_4 ();
+ int rc = client.passes_0_4 ();
+ // Need to give a user a better diagnostic, if she didn't
+ // even ask for a server
+ if (rc && s.automatic_server_mode) {
+ cerr << _("Note: --use-server --unprivileged was selected because of stapusr membership.") << endl;
+ }
+ return rc;
#else
- cerr << _("WARNING: Without NSS, using a compile-server is not supported by this version of systemtap") << endl;
+ s.print_warning("Without NSS, using a compile-server is not supported by this version of systemtap");
// This cannot be an attempt to use a server after a local compile failed
// since --use-server-on-error is locked to 'no' if we don't have
// NSS.
s.kernel_base_release.assign(s.kernel_release, 0, s.kernel_release.find('-'));
- // Now that no further changes to s.kernel_build_tree can occur, let's use it.
- if ((rc = parse_kernel_config (s)) != 0)
+ // Update various paths to include the sysroot, if provided.
+ if (!s.sysroot.empty())
{
- // Try again with a server
- s.set_try_server ();
- return rc;
+ if (s.update_release_sysroot && !s.sysroot.empty())
+ s.kernel_build_tree = s.sysroot + s.kernel_build_tree;
+ debuginfo_path_insert_sysroot(s.sysroot);
}
- if ((rc = parse_kernel_exports (s)) != 0)
+ // Now that no further changes to s.kernel_build_tree can occur, let's use it.
+ if ((rc = s.parse_kernel_config ()) != 0
+ || (rc = s.parse_kernel_exports ()) != 0
+ || (rc = s.parse_kernel_functions ()) != 0)
{
// Try again with a server
s.set_try_server ();
}
// Create the name of the C source file within the temporary
- // directory.
- s.translated_source = string(s.tmpdir) + "/" + s.module_name + ".c";
+ // directory. Note the _src prefix, explained in
+ // buildrun.cxx:compile_pass()
+ s.translated_source = string(s.tmpdir) + "/" + s.module_name + "_src.c";
PROBE1(stap, pass0__end, &s);
struct timeval tv_before;
gettimeofday (&tv_before, NULL);
- // PASS 1a: PARSING USER SCRIPT
+ // PASS 1a: PARSING LIBRARY SCRIPTS
PROBE1(stap, pass1a__start, &s);
+ // We need to handle the library scripts first because this pass
+ // gathers information on .stpm files that might be needed to
+ // parse the user script.
+
+ // We need to first ascertain the status of the user script, though.
struct stat user_file_stat;
int user_file_stat_rc = -1;
if (s.script_file == "-")
{
- s.user_file = parse (s, cin, s.guru_mode);
user_file_stat_rc = fstat (STDIN_FILENO, & user_file_stat);
}
else if (s.script_file != "")
{
- s.user_file = parse (s, s.script_file, s.guru_mode);
user_file_stat_rc = stat (s.script_file.c_str(), & user_file_stat);
}
- else
- {
- istringstream ii (s.cmdline_script);
- s.user_file = parse (s, ii, s.guru_mode);
- }
- if (s.user_file == 0)
+ // otherwise, rc is 0 for a command line script
+
+ vector<string> version_suffixes;
+ if (s.runtime_mode == systemtap_session::kernel_runtime)
{
- // Syntax errors already printed.
- rc ++;
+ // Construct kernel-versioning search path
+ string kvr = s.kernel_release;
+
+ // add full kernel-version-release (2.6.NN-FOOBAR)
+ version_suffixes.push_back ("/" + kvr);
+
+ // add kernel version (2.6.NN)
+ if (kvr != s.kernel_base_release)
+ {
+ kvr = s.kernel_base_release;
+ version_suffixes.push_back ("/" + kvr);
+ }
+
+ // add kernel family (2.6)
+ string::size_type dot1_index = kvr.find ('.');
+ string::size_type dot2_index = kvr.rfind ('.');
+ while (dot2_index > dot1_index && dot2_index != string::npos)
+ {
+ kvr.erase(dot2_index);
+ version_suffixes.push_back ("/" + kvr);
+ dot2_index = kvr.rfind ('.');
+ }
}
- // Construct arch / kernel-versioning search path
- vector<string> version_suffixes;
- string kvr = s.kernel_release;
- const string& arch = s.architecture;
- // add full kernel-version-release (2.6.NN-FOOBAR) + arch
- version_suffixes.push_back ("/" + kvr + "/" + arch);
- version_suffixes.push_back ("/" + kvr);
- // add kernel version (2.6.NN) + arch
- if (kvr != s.kernel_base_release) {
- kvr = s.kernel_base_release;
- version_suffixes.push_back ("/" + kvr + "/" + arch);
- version_suffixes.push_back ("/" + kvr);
- }
- // add kernel family (2.6) + arch
- string::size_type dot1_index = kvr.find ('.');
- string::size_type dot2_index = kvr.rfind ('.');
- while (dot2_index > dot1_index && dot2_index != string::npos) {
- kvr.erase(dot2_index);
- version_suffixes.push_back ("/" + kvr + "/" + arch);
- version_suffixes.push_back ("/" + kvr);
- dot2_index = kvr.rfind ('.');
- }
- // add architecture search path
- version_suffixes.push_back("/" + arch);
// add empty string as last element
version_suffixes.push_back ("");
- // PASS 1b: PARSING LIBRARY SCRIPTS
- PROBE1(stap, pass1b__start, &s);
+ // Add arch variants of every path, just before each
+ const string& arch = s.architecture;
+ for (unsigned i=0; i<version_suffixes.size(); i+=2)
+ version_suffixes.insert(version_suffixes.begin() + i,
+ version_suffixes[i] + "/" + arch);
+
+ // Add runtime variants of every path, before everything else
+ string runtime_prefix;
+ if (s.runtime_mode == systemtap_session::kernel_runtime)
+ runtime_prefix = "/linux";
+ else if (s.runtime_mode == systemtap_session::dyninst_runtime)
+ runtime_prefix = "/dyninst";
+ if (!runtime_prefix.empty())
+ for (unsigned i=0; i<version_suffixes.size(); i+=2)
+ version_suffixes.insert(version_suffixes.begin() + i/2,
+ runtime_prefix + version_suffixes[i]);
+
+ // First, parse .stpm files on the include path. We need to have the
+ // resulting macro definitions available for parsing library files,
+ // but since .stpm files can consist only of '@define' constructs,
+ // we can parse each one without reference to the others.
+ set<pair<dev_t, ino_t> > seen_library_macro_files;
+ set<string> seen_library_macro_files_names;
+
+ for (unsigned i=0; i<s.include_path.size(); i++)
+ {
+ // now iterate upon it
+ for (unsigned k=0; k<version_suffixes.size(); k++)
+ {
+ glob_t globbuf;
+ string dir = s.include_path[i] + version_suffixes[k] + "/*.stpm";
+ int r = glob(dir.c_str (), 0, NULL, & globbuf);
+ if (r == GLOB_NOSPACE || r == GLOB_ABORTED)
+ rc ++;
+ // GLOB_NOMATCH is acceptable
+
+ unsigned prev_s_library_files = s.library_files.size();
+
+ for (unsigned j=0; j<globbuf.gl_pathc; j++)
+ {
+ assert_no_interrupts();
+
+ struct stat tapset_file_stat;
+ int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
+ if (stat_rc == 0 && user_file_stat_rc == 0 &&
+ user_file_stat.st_dev == tapset_file_stat.st_dev &&
+ user_file_stat.st_ino == tapset_file_stat.st_ino)
+ {
+ cerr
+ << _F("usage error: macro tapset file '%s' cannot be run directly as a session script.",
+ globbuf.gl_pathv[j]) << endl;
+ rc ++;
+ }
+
+ // PR11949: duplicate-eliminate tapset files
+ if (stat_rc == 0)
+ {
+ pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev,
+ tapset_file_stat.st_ino);
+ if (seen_library_macro_files.find(here) != seen_library_macro_files.end()) {
+ if (s.verbose>2)
+ clog << _F("Skipping tapset \"%s\", duplicate inode.", globbuf.gl_pathv[j]) << endl;
+ continue;
+ }
+ seen_library_macro_files.insert (here);
+ }
+
+ // PR12443: duplicate-eliminate harder
+ string full_path = globbuf.gl_pathv[j];
+ string tapset_base = s.include_path[i]; // not dir; it has arch suffixes too
+ if (full_path.size() > tapset_base.size()) {
+ string tail_part = full_path.substr(tapset_base.size());
+ if (seen_library_macro_files_names.find (tail_part) != seen_library_macro_files_names.end()) {
+ if (s.verbose>2)
+ clog << _F("Skipping tapset \"%s\", duplicate name.", globbuf.gl_pathv[j]) << endl;
+ continue;
+ }
+ seen_library_macro_files_names.insert (tail_part);
+ }
+
+ if (s.verbose>2)
+ clog << _F("Processing tapset \"%s\"", globbuf.gl_pathv[j]) << endl;
+
+ stapfile* f = parse_library_macros (s, globbuf.gl_pathv[j]);
+ if (f == 0)
+ s.print_warning("macro tapset '" + string(globbuf.gl_pathv[j])
+ + "' has errors, and will be skipped."); // TODOXXX internationalization?
+ else
+ s.library_files.push_back (f);
+ }
+
+ unsigned next_s_library_files = s.library_files.size();
+ if (s.verbose>1 && globbuf.gl_pathc > 0)
+ //TRANSLATORS: Searching through directories, 'processed' means 'examined so far'
+ clog << _F("Searched for library macro files: \"%s\", found: %zu, processed: %u",
+ dir.c_str(), globbuf.gl_pathc,
+ (next_s_library_files-prev_s_library_files)) << endl;
+
+ globfree (&globbuf);
+ }
+ }
+ // Next, gather and parse the library files.
set<pair<dev_t, ino_t> > seen_library_files;
+ set<string> seen_library_files_names;
for (unsigned i=0; i<s.include_path.size(); i++)
{
for (unsigned j=0; j<globbuf.gl_pathc; j++)
{
- if (pending_interrupts)
- break;
+ assert_no_interrupts();
struct stat tapset_file_stat;
int stat_rc = stat (globbuf.gl_pathv[j], & tapset_file_stat);
{
pair<dev_t,ino_t> here = make_pair(tapset_file_stat.st_dev,
tapset_file_stat.st_ino);
- if (seen_library_files.find(here) != seen_library_files.end())
- continue;
+ if (seen_library_files.find(here) != seen_library_files.end()) {
+ if (s.verbose>2)
+ clog << _F("Skipping tapset \"%s\", duplicate inode.", globbuf.gl_pathv[j]) << endl;
+ continue;
+ }
seen_library_files.insert (here);
}
- // XXX: privilege only for /usr/share/systemtap?
- stapfile* f = parse (s, globbuf.gl_pathv[j], true);
- if (f == 0 && !s.suppress_warnings)
+ // PR12443: duplicate-eliminate harder
+ string full_path = globbuf.gl_pathv[j];
+ string tapset_base = s.include_path[i]; // not dir; it has arch suffixes too
+ if (full_path.size() > tapset_base.size()) {
+ string tail_part = full_path.substr(tapset_base.size());
+ if (seen_library_files_names.find (tail_part) != seen_library_files_names.end()) {
+ if (s.verbose>2)
+ clog << _F("Skipping tapset \"%s\", duplicate name.", globbuf.gl_pathv[j]) << endl;
+ continue;
+ }
+ seen_library_files_names.insert (tail_part);
+ }
+
+ if (s.verbose>2)
+ clog << _F("Processing tapset \"%s\"", globbuf.gl_pathv[j]) << endl;
+
+ // NB: we don't need to restrict privilege only for /usr/share/systemtap, i.e.,
+ // excluding user-specified $XDG_DATA_DIRS. That's because stapdev gets
+ // root-equivalent privileges anyway; stapsys and stapusr use a remote compilation
+ // with a trusted environment, where client-side $XDG_DATA_DIRS are not passed.
+
+ stapfile* f = parse (s, globbuf.gl_pathv[j], true /* privileged */);
+ if (f == 0)
s.print_warning("tapset '" + string(globbuf.gl_pathv[j])
- + "' has errors, and will be skipped.");
+ + "' has errors, and will be skipped."); // TODOXXX internationalization?
else
s.library_files.push_back (f);
}
unsigned next_s_library_files = s.library_files.size();
if (s.verbose>1 && globbuf.gl_pathc > 0)
//TRANSLATORS: Searching through directories, 'processed' means 'examined so far'
- clog << _F("Searched: \" %s \", found: %zu, processed: %u",
+ clog << _F("Searched: \"%s\", found: %zu, processed: %u",
dir.c_str(), globbuf.gl_pathc,
(next_s_library_files-prev_s_library_files)) << endl;
if (s.num_errors())
rc ++;
+ // PASS 1b: PARSING USER SCRIPT
+ PROBE1(stap, pass1b__start, &s);
+
+ if (s.script_file == "-")
+ {
+ s.user_file = parse (s, cin, s.guru_mode);
+ }
+ else if (s.script_file != "")
+ {
+ s.user_file = parse (s, s.script_file, s.guru_mode);
+ }
+ else
+ {
+ istringstream ii (s.cmdline_script);
+ s.user_file = parse (s, ii, s.guru_mode);
+ }
+ if (s.user_file == 0)
+ {
+ // Syntax errors already printed.
+ rc ++;
+ }
+
if (rc == 0 && s.last_pass == 1)
{
cout << _("# parse tree dump") << endl;
// syntax errors, if any, are already printed
if (s.verbose)
{
+ // XXX also include a count of helper macro files loaded (.stpm)?
clog << "Pass 1: parsed user script and "
<< s.library_files.size()
<< " library script(s) "
PROBE1(stap, pass1__end, &s);
- if (rc || s.last_pass == 1 || pending_interrupts) return rc;
+ assert_no_interrupts();
+ if (rc || s.last_pass == 1) return rc;
times (& tms_before);
gettimeofday (&tv_before, NULL);
PROBE1(stap, pass2__start, &s);
rc = semantic_pass (s);
+ // Dump a list of known probe point types, if requested.
+ if (s.dump_probe_types)
+ s.pattern_root->dump (s);
+
if (s.listing_mode || (rc == 0 && s.last_pass == 2))
printscript(s, cout);
PROBE1(stap, pass2__end, &s);
- if (rc || s.listing_mode || s.last_pass == 2 || pending_interrupts) return rc;
+ assert_no_interrupts();
+ if (rc || s.listing_mode || s.last_pass == 2) return rc;
rc = prepare_translate_pass (s);
- if (rc || pending_interrupts) return rc;
+ assert_no_interrupts();
+ if (rc) return rc;
// Generate hash. There isn't any point in generating the hash
// if last_pass is 2, since we'll quit before using it.
// See if we can use cached source/module.
if (get_script_from_cache(s))
{
+ // We may still need to build uprobes, if it's not also cached.
+ if (s.need_uprobes)
+ rc = uprobes_pass(s);
+
// If our last pass isn't 5, we're done (since passes 3 and
// 4 just generate what we just pulled out of the cache).
- if (s.last_pass < 5 || pending_interrupts) return rc;
+ assert_no_interrupts();
+ if (rc || s.last_pass < 5) return rc;
// Short-circuit to pass 5.
return 0;
PROBE1(stap, pass3__end, &s);
- if (rc || s.last_pass == 3 || pending_interrupts) return rc;
+ assert_no_interrupts();
+ if (rc || s.last_pass == 3) return rc;
// PASS 4: COMPILATION
s.verbose = s.perpass_verbose[3];
rc = compile_pass (s);
if (! rc && s.last_pass == 4)
{
- cout << ((s.hash_path == "") ? (s.module_name + string(".ko")) : s.hash_path);
+ cout << ((s.hash_path == "") ? s.module_filename() : s.hash_path);
cout << endl;
}
gettimeofday (&tv_after, NULL);
if (s.verbose) clog << "Pass 4: compiled C into \""
- << s.module_name << ".ko"
+ << s.module_filename()
<< "\" "
<< TIMESPRINT
<< endl;
if (rc && ! s.try_server ())
- cerr << _("Pass 4: compilation failed. Try again with another '--vp 0001' option.") << endl;
- //cerr << "Pass 4: compilation failed. "
- // << "Try again with another '--vp 0001' option."
- // << endl;
+ cerr << _("Pass 4: compilation failed. [man error::pass4]") << endl;
+
else
{
// Update cache. Cache cleaning is kicked off at the beginning of this function.
if (s.use_script_cache)
add_script_to_cache(s);
- if (s.use_cache)
+ if (s.use_cache && !s.runtime_usermode_p())
add_stapconf_to_cache(s);
// We may need to save the module in $CWD if the cache was
// 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";
+ string module_src_path = s.tmpdir + "/" + s.module_filename();
+ string module_dest_path = s.module_filename();
copy_file(module_src_path, module_dest_path, s.verbose > 1);
}
}
#endif
}
- // Clean up temporary directory. Obviously, be careful with this.
- remove_temp_dir (s);
-
PROBE1(stap, pass6__end, &s);
}
// Specify default server(s).
s.specified_servers.push_back ("");
- // Remove the previous temporary directory and start fresh.
- remove_temp_dir (s);
+ // Reset the previous temporary directory and start fresh
+ s.reset_tmp_dir();
// Try to compile again, using the server
clog << _("Attempting compilation using a compile server")
<< endl;
+
int rc = passes_0_4 (s);
return rc;
}
main (int argc, char * const argv [])
{
// Initialize defaults.
- systemtap_session s;
-
- setlocale (LC_ALL, "");
- bindtextdomain (PACKAGE, LOCALEDIR);
- textdomain (PACKAGE);
+ try {
+ systemtap_session s;
+
+ setlocale (LC_ALL, "");
+ bindtextdomain (PACKAGE, LOCALEDIR);
+ textdomain (PACKAGE);
+
+ // Set up our handler to catch routine signals, to allow clean
+ // and reasonably timely exit.
+ setup_signals(&handle_interrupt);
+
+ // PR13520: Parse $SYSTEMTAP_DIR/rc for extra options
+ string rc_file = s.data_path + "/rc";
+ ifstream rcf (rc_file.c_str());
+ string rcline;
+ wordexp_t words;
+ memset (& words, 0, sizeof(words));
+ int rc = 0;
+ int linecount = 0;
+ while (getline (rcf, rcline))
+ {
+ rc = wordexp (rcline.c_str(), & words, WRDE_NOCMD|WRDE_UNDEF|
+ (linecount > 0 ? WRDE_APPEND : 0));
+ // NB: WRDE_APPEND automagically reallocates words.* as more options are added.
+ linecount ++;
+ if (rc) break;
+ }
+ int extended_argc = words.we_wordc + argc;
+ char **extended_argv = (char**) calloc (extended_argc + 1, sizeof(char*));
+ if (rc || !extended_argv)
+ {
+ clog << _F("Error processing extra options in %s", rc_file.c_str());
+ return EXIT_FAILURE;
+ }
+ // Copy over the arguments *by reference*, first the ones from the rc file.
+ char **p = & extended_argv[0];
+ *p++ = argv[0];
+ for (unsigned i=0; i<words.we_wordc; i++) *p++ = words.we_wordv[i];
+ for (int j=1; j<argc; j++) *p++ = argv[j];
+ *p++ = NULL;
+
+ // Process the command line.
+ rc = s.parse_cmdline (extended_argc, extended_argv);
+ if (rc != 0)
+ return rc;
- // Set up our handler to catch routine signals, to allow clean
- // and reasonably timely exit.
- setup_signals(&handle_interrupt);
+ if (words.we_wordc > 0 && s.verbose > 1)
+ clog << _F("Extra options in %s: %d\n", rc_file.c_str(), (int)words.we_wordc);
- // Process the command line.
- int rc = s.parse_cmdline (argc, argv);
- if (rc != 0)
- exit (rc);
+ // Check for options conflicts. Exits if errors are detected.
+ s.check_options (extended_argc, extended_argv);
- // Check for options conflicts. Exits if errors are detected.
- s.check_options (argc, argv);
+ // We don't need these strings any more.
+ wordfree (& words);
+ free (extended_argv);
- // arguments parsed; get down to business
- if (s.verbose > 1)
- s.version ();
+ // arguments parsed; get down to business
+ if (s.verbose > 1)
+ s.version ();
- // Some of the remote methods need to write temporary data, so go ahead
- // and create the main tempdir now.
- rc = create_temp_dir (s);
+ // Need to send the verbose message here, rather than in the session ctor, since
+ // we didn't know if verbose was set.
+ if (rc == 0 && s.verbose>1)
+ clog << _F("Created temporary directory \"%s\"", s.tmpdir.c_str()) << endl;
- // Prepare connections for each specified remote target.
- vector<remote*> targets;
- if (s.remote_uris.empty())
- s.remote_uris.push_back("direct:");
- for (unsigned i = 0; rc == 0 && i < s.remote_uris.size(); ++i)
- {
- remote *target = remote::create(s, s.remote_uris[i]);
- if (target)
- targets.push_back(target);
- else
- rc = 1;
- }
+ // Prepare connections for each specified remote target.
+ vector<remote*> targets;
+ bool fake_remote=false;
+ if (s.remote_uris.empty())
+ {
+ fake_remote=true;
+ s.remote_uris.push_back("direct:");
+ }
+ for (unsigned i = 0; rc == 0 && i < s.remote_uris.size(); ++i)
+ {
+ // PR13354: pass remote id#/url only in non --remote=HOST cases
+ remote *target = remote::create(s, s.remote_uris[i],
+ fake_remote ? -1 : (int)i);
+ if (target)
+ targets.push_back(target);
+ else
+ rc = 1;
+ }
- // Discover and loop over each unique session created by the remote targets.
- set<systemtap_session*> sessions;
- for (unsigned i = 0; i < targets.size(); ++i)
- sessions.insert(targets[i]->get_session());
- for (set<systemtap_session*>::iterator it = sessions.begin();
- rc == 0 && !pending_interrupts && it != sessions.end(); ++it)
- {
- systemtap_session& ss = **it;
- if (ss.verbose > 1)
- clog << _F("Session arch: %s release: %s",
- ss.architecture.c_str(), ss.kernel_release.c_str()) << endl;
+ // Discover and loop over each unique session created by the remote targets.
+ set<systemtap_session*> sessions;
+ for (unsigned i = 0; i < targets.size(); ++i)
+ sessions.insert(targets[i]->get_session());
+ for (set<systemtap_session*>::iterator it = sessions.begin();
+ rc == 0 && !pending_interrupts && it != sessions.end(); ++it)
+ {
+ systemtap_session& ss = **it;
+ if (ss.verbose > 1)
+ clog << _F("Session arch: %s release: %s",
+ ss.architecture.c_str(), ss.kernel_release.c_str()) << endl;
- // If requested, query server status. This is independent of other tasks.
- query_server_status (ss);
+#if HAVE_NSS
+ // If requested, query server status. This is independent of other tasks.
+ query_server_status (ss);
- // If requested, manage trust of servers. This is independent of other tasks.
- manage_server_trust (ss);
+ // If requested, manage trust of servers. This is independent of other tasks.
+ manage_server_trust (ss);
+#endif
- // 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 (ss.have_script)
- {
- // Run passes 0-4 for each unique session,
- // either locally or using a compile-server.
- ss.init_try_server ();
- if ((rc = passes_0_4 (ss)))
- {
- // Compilation failed.
- // Try again using a server if appropriate.
- if (ss.try_server ())
- rc = passes_0_4_again_with_server (ss);
- }
- }
- }
+ // Run the passes only if a script has been specified. The requirement for
+ // a script has already been checked in systemtap_session::check_options.
+ // Run the passes also if a dump of supported probe types has been requested via a server.
+ if (ss.have_script || (ss.dump_probe_types && ! s.specified_servers.empty ()))
+ {
+ // Run passes 0-4 for each unique session,
+ // either locally or using a compile-server.
+ ss.init_try_server ();
+ if ((rc = passes_0_4 (ss)))
+ {
+ // Compilation failed.
+ // Try again using a server if appropriate.
+ if (ss.try_server ())
+ rc = passes_0_4_again_with_server (ss);
+ }
+ }
+ else if (ss.dump_probe_types)
+ {
+ // Dump a list of known probe point types, if requested.
+ register_standard_tapsets(ss);
+ ss.pattern_root->dump (ss);
+ }
+ }
- // Run pass 5, if requested
- if (rc == 0 && s.have_script && s.last_pass >= 5 && ! pending_interrupts)
- rc = pass_5 (s, targets);
+ // Run pass 5, if requested
+ if (rc == 0 && s.have_script && s.last_pass >= 5 && ! pending_interrupts)
+ rc = pass_5 (s, targets);
- // Pass 6. Cleanup
- for (unsigned i = 0; i < targets.size(); ++i)
- delete targets[i];
- cleanup (s, rc);
+ // Pass 6. Cleanup
+ for (unsigned i = 0; i < targets.size(); ++i)
+ delete targets[i];
+ cleanup (s, rc);
- return (rc||pending_interrupts) ? EXIT_FAILURE : EXIT_SUCCESS;
+ assert_no_interrupts();
+ return (rc) ? EXIT_FAILURE : EXIT_SUCCESS;
+ }
+ catch (const interrupt_exception& e) {
+ // User entered ctrl-c, exit quietly.
+ return EXIT_FAILURE;
+ }
+ catch (const exit_exception& e) {
+ // Exiting for any quiet reason.
+ return e.rc;
+ }
+ catch (const runtime_error &e) {
+ // Some other uncaught runtime_error exception.
+ cerr << e.what() << endl;
+ return EXIT_FAILURE;
+ }
+ catch (...) {
+ // Catch all other unknown exceptions.
+ cerr << _("ERROR: caught unknown exception!") << endl;
+ return EXIT_FAILURE;
+ }
}
/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */