]> sourceware.org Git - systemtap.git/blame - stapdyn/dynutil.cxx
update copyrights
[systemtap.git] / stapdyn / dynutil.cxx
CommitLineData
fce2d171 1// stapdyn utility functions
ef36f781 2// Copyright (C) 2014 Red Hat Inc.
fce2d171
JS
3//
4// This file is part of systemtap, and is free software. You can
5// redistribute it and/or modify it under the terms of the GNU General
6// Public License (GPL); either version 2, or (at your option) any
7// later version.
8
51030d84
JS
9#include "config.h"
10
fce2d171 11#include <iostream>
0ed66feb
JS
12#include <string>
13#include <cstdlib>
14
15extern "C" {
fce2d171 16#include <dlfcn.h>
0ed66feb
JS
17#include <err.h>
18#include <link.h>
19}
20
51030d84
JS
21#ifdef HAVE_SELINUX
22#include <selinux/selinux.h>
23#endif
24
0ed66feb
JS
25#include "dynutil.h"
26#include "../util.h"
27
28using namespace std;
29
30
31// Callback for dl_iterate_phdr to look for libdyninstAPI.so
32static int
33guess_dyninst_rt_callback(struct dl_phdr_info *info,
34 size_t size __attribute__ ((unused)),
35 void *data)
36{
37 string& libdyninstAPI = *static_cast<string*>(data);
38
39 const string name = info->dlpi_name ?: "(null)";
40 if (name.find("libdyninstAPI.so") != string::npos)
41 libdyninstAPI = name;
42
43 return 0;
44}
45
46// Look for libdyninstAPI.so in our own process, and use that
47// to guess the path for libdyninstAPI_RT.so
48static const string
49guess_dyninst_rt(void)
50{
51 string libdyninstAPI;
52 dl_iterate_phdr(guess_dyninst_rt_callback, &libdyninstAPI);
53
54 string libdyninstAPI_RT;
55 size_t so = libdyninstAPI.rfind(".so");
56 if (so != string::npos)
57 {
58 libdyninstAPI_RT = libdyninstAPI;
59 libdyninstAPI_RT.insert(so, "_RT");
60 }
61 return libdyninstAPI_RT;
62}
63
64// Check that environment DYNINSTAPI_RT_LIB exists and is a valid file.
65// If not, try to guess a good value and set it.
66bool
67check_dyninst_rt(void)
68{
69 static const char rt_env_name[] = "DYNINSTAPI_RT_LIB";
70 const char* rt_env = getenv(rt_env_name);
71 if (rt_env)
72 {
73 if (file_exists(rt_env))
74 return true;
145004e9 75 staperror() << "Invalid " << rt_env_name << ": \"" << rt_env << "\"" << endl;
0ed66feb
JS
76 }
77
78 const string rt = guess_dyninst_rt();
79 if (rt.empty() || !file_exists(rt))
80 {
145004e9 81 staperror() << "Can't find libdyninstAPI_RT.so; try setting " << rt_env_name << endl;
0ed66feb
JS
82 return false;
83 }
84
85 if (setenv(rt_env_name, rt.c_str(), 1) != 0)
86 {
145004e9
JL
87 int olderrno = errno;
88 staperror() << "Can't set " << rt_env_name << ": " << strerror(olderrno);
0ed66feb
JS
89 return false;
90 }
91
92 return true;
93}
94
95
51030d84
JS
96// Check that SELinux settings are ok for Dyninst operation.
97bool
56098c79 98check_dyninst_sebools(bool attach)
51030d84
JS
99{
100#ifdef HAVE_SELINUX
101 // For all these checks, we could examine errno on failure to act differently
102 // for e.g. ENOENT vs. EPERM. But since these are just early diagnostices,
103 // I'm only going worry about successful bools for now.
104
105 // deny_ptrace is definitely a blocker for us to attach at all
106 if (security_get_boolean_active("deny_ptrace") > 0)
107 {
145004e9
JL
108 staperror() << "SELinux boolean 'deny_ptrace' is active, "
109 "which blocks Dyninst" << endl;
51030d84
JS
110 return false;
111 }
112
113 // We might have to get more nuanced about allow_execstack, especially if
114 // Dyninst is later enhanced to work around this restriction. But for now,
115 // this is also a blocker.
116 if (security_get_boolean_active("allow_execstack") == 0)
117 {
145004e9
JL
118 staperror() << "SELinux boolean 'allow_execstack' is disabled, "
119 "which blocks Dyninst" << endl;
51030d84
JS
120 return false;
121 }
56098c79
JS
122
123 // In process-attach mode, SELinux will trigger "avc: denied { execmod }"
124 // on ld.so, when the mutator is injecting the dlopen for libdyninstAPI_RT.so.
125 if (attach && security_get_boolean_active("allow_execmod") == 0)
126 {
145004e9
JL
127 staperror() << "SELinux boolean 'allow_execmod' is disabled, "
128 "which blocks Dyninst" << endl;
56098c79
JS
129 return false;
130 }
145ceff0
JS
131#else
132 (void)attach; // unused
51030d84
JS
133#endif
134
135 return true;
136}
137
138
3d381237
JS
139// Check whether a process exited cleanly
140bool
141check_dyninst_exit(BPatch_process *process)
142{
143 int code;
144 switch (process->terminationStatus())
145 {
146 case ExitedNormally:
147 code = process->getExitCode();
148 if (code == EXIT_SUCCESS)
149 return true;
0d06fa0d 150 stapwarn() << "Child process exited with status " << code << endl;
3d381237
JS
151 return false;
152
153 case ExitedViaSignal:
154 code = process->getExitSignal();
0d06fa0d
JL
155 stapwarn() << "Child process exited with signal " << code
156 << " (" << strsignal(code) << ")" << endl;
3d381237
JS
157 return false;
158
6d27dcff
JS
159 case NoExit:
160 if (process->isTerminated())
0d06fa0d 161 stapwarn() << "Child process exited in an unknown manner" << endl;
6d27dcff 162 else
0d06fa0d 163 stapwarn() << "Child process has not exited" << endl;
6d27dcff
JS
164 return false;
165
3d381237
JS
166 default:
167 return false;
168 }
169}
170
171
fce2d171
JS
172// Get an untyped symbol from a dlopened module.
173// If flagged as 'required', throw an exception if missing or NULL.
174void *
175get_dlsym(void* handle, const char* symbol, bool required)
176{
177 const char* err = dlerror(); // clear previous errors
178 void *pointer = dlsym(handle, symbol);
179 if (required)
180 {
181 if ((err = dlerror()))
182 throw std::runtime_error("dlsym " + std::string(err));
183 if (pointer == NULL)
184 throw std::runtime_error("dlsym " + std::string(symbol) + " is NULL");
185 }
186 return pointer;
187}
188
189
190//
191// Logging, warnings, and errors, oh my!
192//
193
194// A null-sink output stream, similar to /dev/null
195// (no buffer -> badbit -> quietly suppressed output)
196static ostream nullstream(NULL);
197
198// verbosity, increased by -v
199unsigned stapdyn_log_level = 0;
200
201// Whether to suppress warnings, set by -w
ac033e87 202bool stapdyn_suppress_warnings = false;
fce2d171 203
6d842dc7
DS
204// Output file name, set by -o
205char *stapdyn_outfile_name = NULL;
206
fce2d171
JS
207// Return a stream for logging at the given verbosity level.
208ostream&
209staplog(unsigned level)
210{
211 if (level > stapdyn_log_level)
212 return nullstream;
213 return clog << program_invocation_short_name << ": ";
214}
215
216// Return a stream for warning messages.
217ostream&
218stapwarn(void)
219{
ac033e87 220 if (stapdyn_suppress_warnings)
fce2d171 221 return nullstream;
bfbd8838
JL
222 return clog << program_invocation_short_name << ": "
223 << colorize("WARNING:", "warning") << " ";
fce2d171
JS
224}
225
226// Return a stream for error messages.
227ostream&
228staperror(void)
229{
bfbd8838
JL
230 return clog << program_invocation_short_name << ": "
231 << colorize("ERROR:", "error") << " ";
fce2d171
JS
232}
233
89fd17f5
JL
234// Whether to color error and warning messages
235bool color_errors; // Initialized in main()
fce2d171 236
bfbd8838
JL
237// Adds coloring to strings
238std::string
239colorize(std::string str, std::string type)
240{
241 if (str.empty() || !color_errors)
242 return str;
243 else {
244 // Check if this type is defined in SYSTEMTAP_COLORS
245 std::string color = parse_stap_color(type);
246 if (!color.empty()) // no need to pollute terminal if not necessary
247 return "\033[" + color + "m\033[K" + str + "\033[m\033[K";
248 else
249 return str;
250 }
251}
252
253/* Parse SYSTEMTAP_COLORS and returns the SGR parameter(s) for the given
254type. The env var SYSTEMTAP_COLORS must be in the following format:
255'key1=val1:key2=val2:' etc... where valid keys are 'error', 'warning',
256'source', 'caret', 'token' and valid values constitute SGR parameter(s).
257For example, the default setting would be:
258'error=01;31:warning=00;33:source=00;34:caret=01:token=01'
259*/
260std::string
261parse_stap_color(std::string type)
262{
263 const char *key, *col, *eq;
264 int n = type.size();
265 int done = 0;
266
267 key = getenv("SYSTEMTAP_COLORS");
6667be37 268 if (key == NULL)
bfbd8838 269 key = "error=01;31:warning=00;33:source=00;34:caret=01:token=01";
6667be37
JL
270 else if (*key == '\0')
271 return ""; // disable colors if set but empty
bfbd8838
JL
272
273 while (!done) {
274 if (!(col = strchr(key, ':'))) {
275 col = strchr(key, '\0');
276 done = 1;
277 }
278 if (!((eq = strchr(key, '=')) && eq < col))
279 return ""; /* invalid syntax: no = in range */
280 if (!(key < eq && eq < col-1))
281 return ""; /* invalid syntax: key or val empty */
282 if (strspn(eq+1, "0123456789;") < (size_t)(col-eq-1))
283 return ""; /* invalid syntax: invalid char in val */
284 if (eq-key == n && type.compare(0, n, key, n) == 0)
285 return string(eq+1, col-eq-1);
286 if (!done) key = col+1; /* advance to next key */
287 }
288
289 // Could not find the key
290 return "";
291}
292
0ed66feb 293/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.095294 seconds and 5 git commands to generate.