Branch data Line data Source code
1 : : // Copyright (C) Andrew Tridgell 2002 (original file)
2 : : // Copyright (C) 2006-2011 Red Hat Inc. (systemtap changes)
3 : : //
4 : : // This program is free software; you can redistribute it and/or
5 : : // modify it under the terms of the GNU General Public License as
6 : : // published by the Free Software Foundation; either version 2 of the
7 : : // License, or (at your option) any later version.
8 : : //
9 : : // This program is distributed in the hope that it will be useful, but
10 : : // WITHOUT ANY WARRANTY; without even the implied warranty of
11 : : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the GNU
12 : : // General Public License for more details.
13 : : //
14 : : // You should have received a copy of the GNU General Public License
15 : : // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 : :
17 : : #include "util.h"
18 : : #include "stap-probe.h"
19 : : #include <stdexcept>
20 : : #include <cerrno>
21 : : #include <map>
22 : : #include <set>
23 : : #include <string>
24 : : #include <fstream>
25 : : #include <cassert>
26 : : #include <ext/stdio_filebuf.h>
27 : :
28 : : extern "C" {
29 : : #include <elf.h>
30 : : #include <fcntl.h>
31 : : #include <grp.h>
32 : : #include <pwd.h>
33 : : #include <spawn.h>
34 : : #include <stdio.h>
35 : : #include <stdlib.h>
36 : : #include <sys/stat.h>
37 : : #include <sys/types.h>
38 : : #include <sys/wait.h>
39 : : #include <unistd.h>
40 : : #include <regex.h>
41 : : #include <stdarg.h>
42 : : #ifndef SINGLE_THREADED
43 : : #include <pthread.h>
44 : : #endif
45 : : }
46 : :
47 : : using namespace std;
48 : : using namespace __gnu_cxx;
49 : :
50 : :
51 : : // Return current users home directory or die.
52 : : const char *
53 : 70 : get_home_directory(void)
54 : : {
55 : 70 : const char *p = getenv("HOME");
56 [ + - ]: 70 : if (p)
57 : 70 : return p;
58 : :
59 : 0 : struct passwd *pwd = getpwuid(getuid());
60 [ # # ]: 0 : if (pwd)
61 : 0 : return pwd->pw_dir;
62 : :
63 : 0 : cerr << _("Unable to determine home directory") << endl;
64 : 70 : return "/";
65 : : }
66 : :
67 : :
68 : : // Get the size of a file in bytes
69 : : size_t
70 : 4185 : get_file_size(const string &path)
71 : : {
72 : : struct stat file_info;
73 : :
74 [ + - ][ + + ]: 4185 : if (stat(path.c_str(), &file_info) == 0)
75 : 4150 : return file_info.st_size;
76 : : else
77 : 4185 : return 0;
78 : : }
79 : :
80 : : // Get the size of a file in bytes
81 : : size_t
82 : 988 : get_file_size(int fd)
83 : : {
84 : : struct stat file_info;
85 : :
86 [ + - ]: 988 : if (fstat(fd, &file_info) == 0)
87 : 988 : return file_info.st_size;
88 : : else
89 : 988 : return 0;
90 : : }
91 : :
92 : : // Check that a file is present
93 : : bool
94 : 5281 : file_exists (const string &path)
95 : : {
96 : : struct stat file_info;
97 : :
98 [ + - ][ + + ]: 5281 : if (stat(path.c_str(), &file_info) == 0)
99 : 4349 : return true;
100 : :
101 : 5281 : return false;
102 : : }
103 : :
104 : : // Copy a file. The copy is done via a temporary file and atomic
105 : : // rename.
106 : : bool
107 : 2856 : copy_file(const string& src, const string& dest, bool verbose)
108 : : {
109 : : int fd1, fd2;
110 : : char buf[10240];
111 : : int n;
112 [ + - ]: 2856 : string tmp;
113 : : char *tmp_name;
114 : : mode_t mask;
115 : :
116 [ + + ]: 2856 : if (verbose)
117 [ + - ][ + - ]: 48 : clog << _F("Copying %s to %s", src.c_str(), dest.c_str()) << endl;
[ + - ][ + - ]
[ + - ][ + - ]
118 : :
119 : : // Open the src file.
120 [ + - ][ + - ]: 2856 : fd1 = open(src.c_str(), O_RDONLY);
121 [ - + ]: 2856 : if (fd1 == -1)
122 : 0 : goto error;
123 : :
124 : : // Open the temporary output file.
125 [ + - ][ + - ]: 2856 : tmp = dest + string(".XXXXXX");
[ + - ][ + - ]
[ + - ]
126 [ + - ]: 2856 : tmp_name = (char *)tmp.c_str();
127 [ + - ]: 2856 : fd2 = mkstemp(tmp_name);
128 [ - + ]: 2856 : if (fd2 == -1)
129 : : {
130 [ # # ]: 0 : close(fd1);
131 : 0 : goto error;
132 : : }
133 : :
134 : : // Copy the src file to the temporary output file.
135 [ + - ][ + + ]: 98724 : while ((n = read(fd1, buf, sizeof(buf))) > 0)
136 : : {
137 [ + - ][ - + ]: 95868 : if (write(fd2, buf, n) != n)
138 : : {
139 [ # # ]: 0 : close(fd2);
140 [ # # ]: 0 : close(fd1);
141 : 0 : unlink(tmp_name);
142 : 0 : goto error;
143 : : }
144 : : }
145 [ + - ]: 2856 : close(fd1);
146 : :
147 : : // Set the permissions on the temporary output file.
148 : 2856 : mask = umask(0);
149 : 2856 : fchmod(fd2, 0666 & ~mask);
150 : 2856 : umask(mask);
151 : :
152 : : // Close the temporary output file. The close can fail on NFS if
153 : : // out of space.
154 [ + - ][ - + ]: 2856 : if (close(fd2) == -1)
155 : : {
156 : 0 : unlink(tmp_name);
157 : 0 : goto error;
158 : : }
159 : :
160 : : // Rename the temporary output file to the destination file.
161 [ + - ]: 2856 : unlink(dest.c_str());
162 [ + - ][ - + ]: 2856 : if (rename(tmp_name, dest.c_str()) == -1)
163 : : {
164 : 0 : unlink(tmp_name);
165 : 0 : goto error;
166 : : }
167 : :
168 : 2856 : return true;
169 : :
170 : : error:
171 [ # # ][ # # ]: 0 : cerr << _F("Copy failed (\"%s\" to \"%s\"): %s", src.c_str(),
[ # # ][ # # ]
[ # # ]
172 [ # # ]: 0 : dest.c_str(), strerror(errno)) << endl;
173 [ + - ]: 2856 : return false;
174 : : }
175 : :
176 : :
177 : : // Make sure a directory exists.
178 : : int
179 : 15172 : create_dir(const char *dir, int mode)
180 : : {
181 : : struct stat st;
182 [ + + ]: 15172 : if (stat(dir, &st) == 0)
183 : : {
184 [ + + ]: 15078 : if (S_ISDIR(st.st_mode))
185 : 15076 : return 0;
186 : 2 : errno = ENOTDIR;
187 : 2 : return 1;
188 : : }
189 : :
190 : : // Create the directory. We must create each component
191 : : // of the path ourselves.
192 [ + - ]: 94 : vector<string> components;
193 [ + - ][ + - ]: 94 : tokenize (dir, components, "/");
[ + - ][ + - ]
[ + - ]
194 [ + - ]: 94 : string path;
195 [ + - ]: 94 : if (*dir == '/')
196 : : {
197 : : // Absolute path
198 [ + - ]: 94 : path = "/";
199 : : }
200 : 94 : unsigned limit = components.size ();
201 [ - + ]: 94 : assert (limit != 0);
202 [ + + ]: 607 : for (unsigned ix = 0; ix < limit; ++ix)
203 : : {
204 [ + - ][ + - ]: 513 : path += components[ix] + '/';
[ + - ]
205 [ + - ][ + + ]: 513 : if (mkdir(path.c_str (), mode) != 0 && errno != EEXIST)
[ - + ][ - + ]
206 : 0 : return 1;
207 : : }
208 : :
209 [ + - ][ + - ]: 15172 : return 0;
210 : : }
211 : :
212 : : // Remove a file or directory
213 : : int
214 : 2 : remove_file_or_dir (const char *name)
215 : : {
216 : : int rc;
217 : : struct stat st;
218 : :
219 [ + - ]: 2 : if ((rc = stat(name, &st)) != 0)
220 : : {
221 [ + - ]: 2 : if (errno == ENOENT)
222 : 2 : return 0;
223 : 0 : return 1;
224 : : }
225 : :
226 [ # # ]: 0 : if (remove (name) != 0)
227 : 0 : return 1;
228 : :
229 : 2 : return 0;
230 : : }
231 : :
232 : : /* Obtain the gid of the given group. */
233 : 0 : gid_t get_gid (const char *group_name)
234 : : {
235 : : struct group *stgr;
236 : : /* If we couldn't find the group, return an invalid number. */
237 : 0 : stgr = getgrnam(group_name);
238 [ # # ]: 0 : if (stgr == NULL)
239 : 0 : return (gid_t)-1;
240 : 0 : return stgr->gr_gid;
241 : : }
242 : :
243 : : // Determine whether the current user is in the given group
244 : : // by gid.
245 : : bool
246 : 0 : in_group_id (gid_t target_gid)
247 : : {
248 : : // According to the getgroups() man page, getgroups() may not
249 : : // return the effective gid, so try to match it first. */
250 [ # # ]: 0 : if (target_gid == getegid())
251 : 0 : return true;
252 : :
253 : : // Get the list of the user's groups.
254 : 0 : int ngids = getgroups(0, 0); // Returns the number to allocate.
255 [ # # ]: 0 : if (ngids > 0) {
256 : 0 : gid_t gidlist[ngids];
257 : 0 : ngids = getgroups(ngids, gidlist);
258 [ # # ][ # # ]: 0 : for (int i = 0; i < ngids; i++) {
259 : : // If the user is a member of the target group, then we're done.
260 [ # # ]: 0 : if (gidlist[i] == target_gid)
261 : 0 : return true;
262 : : }
263 : : }
264 [ # # ]: 0 : if (ngids < 0) {
265 : 0 : cerr << _("Unable to retrieve group list") << endl;
266 : 0 : return false;
267 : : }
268 : :
269 : : // The user is not a member of the target group
270 : 0 : return false;
271 : : }
272 : :
273 : : /*
274 : : * Returns a string describing memory resource usage.
275 : : * Since it seems getrusage() doesn't maintain the mem related fields,
276 : : * this routine parses /proc/self/statm to get the statistics.
277 : : */
278 : : string
279 : 294 : getmemusage ()
280 : : {
281 [ + + ][ + - ]: 294 : static long sz = sysconf(_SC_PAGESIZE);
282 : :
283 : : long pages;
284 [ + - ]: 294 : ostringstream oss;
285 [ + - ]: 294 : ifstream statm("/proc/self/statm");
286 [ + - ]: 294 : statm >> pages;
287 : 294 : long kb1 = pages * sz / 1024; // total program size; vmsize
288 [ + - ]: 294 : statm >> pages;
289 : 294 : long kb2 = pages * sz / 1024; // resident set size; vmrss
290 [ + - ]: 294 : statm >> pages;
291 : 294 : long kb3 = pages * sz / 1024; // shared pages
292 [ + - ]: 294 : statm >> pages;
293 : 294 : long kb4 = pages * sz / 1024; // text
294 [ + - ]: 294 : statm >> pages;
295 : : (void) kb4;
296 : 294 : long kb5 = pages * sz / 1024; // library
297 [ + - ]: 294 : statm >> pages;
298 : : (void) kb5;
299 : 294 : long kb6 = pages * sz / 1024; // data+stack
300 [ + - ]: 294 : statm >> pages;
301 : 294 : long kb7 = pages * sz / 1024; // dirty
302 : : (void) kb7;
303 : :
304 [ + - ][ + - ]: 294 : oss << _F("using %ldvirt/%ldres/%ldshr/%lddata kb, ", kb1, kb2, kb3, kb6);
[ + - ]
305 [ + - ][ + - ]: 294 : return oss.str();
[ + - ]
306 : : }
307 : :
308 : : void
309 : 14500865 : tokenize(const string& str, vector<string>& tokens,
310 : : const string& delimiters = " ")
311 : : {
312 : : // Skip delimiters at beginning.
313 : 14500865 : string::size_type lastPos = str.find_first_not_of(delimiters, 0);
314 : : // Find first "non-delimiter".
315 : 14500865 : string::size_type pos = str.find_first_of(delimiters, lastPos);
316 : :
317 [ + + ][ + + ]: 72469752 : while (pos != string::npos || lastPos != string::npos)
[ + + ]
318 : : {
319 : : // Found a token, add it to the vector.
320 [ + - ]: 57968887 : tokens.push_back(str.substr(lastPos, pos - lastPos));
321 : : // Skip delimiters. Note the "not_of"
322 : 57968887 : lastPos = str.find_first_not_of(delimiters, pos);
323 : : // Find next "non-delimiter"
324 : 57968887 : pos = str.find_first_of(delimiters, lastPos);
325 : : }
326 : 14500865 : }
327 : :
328 : : // Akin to tokenize(...,...), but allow tokens before the first delimeter, after the
329 : : // last delimiter and allow internal empty tokens
330 : : void
331 : 11 : tokenize_full(const string& str, vector<string>& tokens,
332 : : const string& delimiters = " ")
333 : : {
334 : : // Check for an empty string or a string of length 1. Neither can have the requested
335 : : // components.
336 [ - + ]: 11 : if (str.size() <= 1)
337 : 0 : return;
338 : :
339 : : // Find the first delimeter.
340 : 11 : string::size_type lastPos = 0;
341 : 11 : string::size_type pos = str.find_first_of(delimiters, lastPos);
342 [ + + ]: 11 : if (pos == string::npos)
343 : 2 : return; // no delimeters
344 : :
345 : : /* No leading empty component allowed. */
346 [ - + ]: 9 : if (pos == lastPos)
347 : 0 : ++lastPos;
348 : :
349 [ - + ]: 9 : assert (lastPos < str.size());
350 [ + - ]: 9 : do
351 : : {
352 : 18 : pos = str.find_first_of(delimiters, lastPos);
353 [ + + ]: 18 : if (pos == string::npos)
354 : 9 : break; // Final trailing component
355 : : // Found a token, add it to the vector.
356 [ + - ]: 9 : tokens.push_back(str.substr (lastPos, pos - lastPos));
357 : : // Skip the delimiter.
358 : 9 : lastPos = pos + 1;
359 : : }
360 : 9 : while (lastPos < str.size());
361 : :
362 : : // A final non-delimited token, if it is not empty.
363 [ + - ]: 9 : if (lastPos < str.size())
364 : : {
365 [ - + ]: 9 : assert (pos == string::npos);
366 [ + - ]: 11 : tokens.push_back(str.substr (lastPos));
367 : : }
368 : : }
369 : :
370 : : // Akin to tokenize(...,"::"), but it also has to deal with C++ template
371 : : // madness. We do this naively by balancing '<' and '>' characters. This
372 : : // doesn't eliminate blanks either, so a leading ::scope still works.
373 : : void
374 : 0 : tokenize_cxx(const string& str, vector<string>& tokens)
375 : : {
376 : 0 : int angle_count = 0;
377 : 0 : string::size_type pos = 0;
378 : 0 : string::size_type colon_pos = str.find("::");
379 : 0 : string::size_type angle_pos = str.find_first_of("<>");
380 [ # # ][ # # ]: 0 : while (colon_pos != string::npos &&
[ # # ][ # # ]
381 : : (angle_count == 0 || angle_pos != string::npos))
382 : : {
383 [ # # ][ # # ]: 0 : if (angle_count > 0 || angle_pos < colon_pos)
384 : : {
385 [ # # ]: 0 : angle_count += str.at(angle_pos) == '<' ? 1 : -1;
386 : 0 : colon_pos = str.find("::", angle_pos + 1);
387 : 0 : angle_pos = str.find_first_of("<>", angle_pos + 1);
388 : : }
389 : : else
390 : : {
391 [ # # ]: 0 : tokens.push_back(str.substr(pos, colon_pos - pos));
392 : 0 : pos = colon_pos + 2;
393 : 0 : colon_pos = str.find("::", pos);
394 : 0 : angle_pos = str.find_first_of("<>", pos);
395 : : }
396 : : }
397 [ # # ]: 0 : tokens.push_back(str.substr(pos));
398 : 0 : }
399 : :
400 : :
401 : : // Resolve an executable name to a canonical full path name, with the
402 : : // same policy as execvp(). A program name not containing a slash
403 : : // will be searched along the $PATH.
404 : :
405 : 880 : string find_executable(const string& name)
406 : : {
407 [ + - ]: 880 : const map<string, string> sysenv;
408 [ + - ][ + - ]: 880 : return find_executable(name, "", sysenv);
[ + - ][ + - ]
[ + - ][ + - ]
409 : : }
410 : :
411 : 1154 : string find_executable(const string& name, const string& sysroot,
412 : : const map<string, string>& sysenv,
413 : : const string& env_path)
414 : : {
415 [ + - ]: 1154 : string retpath;
416 : :
417 [ + - ][ + + ]: 1154 : if (name.size() == 0)
418 [ + - ]: 1 : return name;
419 : :
420 : : struct stat st;
421 : :
422 [ + - ][ + + ]: 1153 : if (name.find('/') != string::npos) // slash in the path already?
423 : : {
424 [ + - ][ + - ]: 204 : retpath = sysroot + name;
[ + - ]
425 : : }
426 : : else // Nope, search $PATH.
427 : : {
428 : : const char *path;
429 [ + - ][ - + ]: 949 : if (sysenv.count(env_path) != 0)
430 [ # # ][ # # ]: 0 : path = sysenv.find(env_path)->second.c_str();
[ # # ]
431 : : else
432 [ + - ]: 949 : path = getenv(env_path.c_str());
433 [ + - ]: 949 : if (path)
434 : : {
435 : : // Split PATH up.
436 [ + - ]: 949 : vector<string> dirs;
437 [ + - ][ + - ]: 949 : tokenize(string(path), dirs, string(":"));
[ + - ][ + - ]
[ + - ]
438 : :
439 : : // Search the path looking for the first executable of the right name.
440 [ + - ][ + - ]: 4248 : for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
[ + - ][ + - ]
[ + + ]
441 : : {
442 [ + - ][ + - ]: 3299 : string fname = sysroot + *i + "/" + name;
[ + - ][ + - ]
[ + - ]
443 [ + - ]: 3299 : const char *f = fname.c_str();
444 : :
445 : : // Look for a normal executable file.
446 [ + + + - ]: 4238 : if (access(f, X_OK) == 0
[ + - ][ + + ]
447 : 939 : && stat(f, &st) == 0
448 : : && S_ISREG(st.st_mode))
449 : : {
450 [ + - ]: 3299 : retpath = fname;
451 : : break;
452 : : }
453 [ + - ][ + + ]: 4248 : }
[ + - ]
454 : : }
455 : : }
456 : :
457 : :
458 : : // Could not find the program on the $PATH. We'll just fall back to
459 : : // the unqualified name, which our caller will probably fail with.
460 [ + - ][ + + ]: 1153 : if (retpath == "")
461 [ + - ][ + - ]: 10 : retpath = sysroot + name;
[ + - ]
462 : :
463 : : // Canonicalize the path name.
464 [ + - ]: 1153 : char *cf = canonicalize_file_name (retpath.c_str());
465 [ + + ]: 1153 : if (cf)
466 : : {
467 [ + - ]: 1137 : string scf = string(cf);
468 [ + - ][ + - ]: 1137 : if (sysroot.empty())
469 [ + - ]: 1137 : retpath = scf;
470 : : else {
471 [ # # ]: 0 : int pos = scf.find(sysroot);
472 [ # # ]: 0 : if (pos == 0)
473 [ # # ]: 0 : retpath = scf;
474 : : else
475 [ # # ][ # # ]: 0 : throw runtime_error(_F("find_executable(): file %s not in sysroot %s", cf, sysroot.c_str()));
[ # # ]
476 : : }
477 [ + - ]: 1137 : free (cf);
478 : : }
479 : :
480 [ + - ][ + - ]: 1154 : return retpath;
481 : : }
482 : :
483 : :
484 : :
485 : 70 : const string cmdstr_quoted(const string& cmd)
486 : : {
487 : : // original cmd : substr1
488 : : // or : substr1'substr2
489 : : // or : substr1'substr2'substr3......
490 : : // after quoted :
491 : : // every substr(even it's empty) is quoted by ''
492 : : // every single-quote(') is quoted by ""
493 : : // examples: substr1 --> 'substr1'
494 : : // substr1'substr2 --> 'substr1'"'"'substr2'
495 : :
496 [ + - ]: 70 : string quoted_cmd;
497 [ + - ]: 70 : string quote("'");
498 [ + - ]: 70 : string replace("'\"'\"'");
499 : 70 : string::size_type pos = 0;
500 : :
501 [ + - ]: 70 : quoted_cmd += quote;
502 [ + - ][ # # ]: 70 : for (string::size_type quote_pos = cmd.find(quote, pos);
[ - + ]
503 : : quote_pos != string::npos;
504 : : quote_pos = cmd.find(quote, pos)) {
505 [ # # ][ # # ]: 0 : quoted_cmd += cmd.substr(pos, quote_pos - pos);
[ # # ]
506 [ # # ]: 0 : quoted_cmd += replace;
507 : 0 : pos = quote_pos + 1;
508 : : }
509 [ + - ][ + - ]: 70 : quoted_cmd += cmd.substr(pos, cmd.length() - pos);
[ + - ][ + - ]
510 [ + - ]: 70 : quoted_cmd += quote;
511 : :
512 [ + - ][ + - ]: 70 : return quoted_cmd;
513 : : }
514 : :
515 : :
516 : : const string
517 : 0 : cmdstr_join(const vector<string>& cmds)
518 : : {
519 [ # # ][ # # ]: 0 : if (cmds.empty())
520 [ # # ][ # # ]: 0 : throw runtime_error(_("cmdstr_join called with an empty command!"));
521 : :
522 [ # # ]: 0 : stringstream cmd;
523 [ # # ][ # # ]: 0 : cmd << cmdstr_quoted(cmds[0]);
[ # # ]
524 [ # # ]: 0 : for (size_t i = 1; i < cmds.size(); ++i)
525 [ # # ][ # # ]: 0 : cmd << " " << cmdstr_quoted(cmds[i]);
[ # # ][ # # ]
526 : :
527 [ # # ][ # # ]: 0 : return cmd.str();
528 : : }
529 : :
530 : :
531 : : // signal-safe set of pids
532 : : class spawned_pids_t {
533 : : private:
534 : : set<pid_t> pids;
535 : : #ifndef SINGLE_THREADED
536 : : pthread_mutex_t mux_pids;
537 : : #endif
538 : :
539 : : public:
540 : 52 : bool contains (pid_t p)
541 : : {
542 : 52 : stap_sigmasker masked;
543 : :
544 : : #ifndef SINGLE_THREADED
545 : 52 : pthread_mutex_lock(&mux_pids);
546 : : #endif
547 [ + - ]: 52 : bool ret = (pids.count(p)==0) ? true : false;
548 : : #ifndef SINGLE_THREADED
549 : 52 : pthread_mutex_unlock(&mux_pids);
550 : : #endif
551 : :
552 : 52 : return ret;
553 : : }
554 : 3860 : bool insert (pid_t p)
555 : : {
556 : 3860 : stap_sigmasker masked;
557 : :
558 : : #ifndef SINGLE_THREADED
559 : 3860 : pthread_mutex_lock(&mux_pids);
560 : : #endif
561 [ + - ][ + - ]: 3860 : bool ret = (p > 0) ? pids.insert(p).second : false;
[ + - ][ # # ]
562 : : #ifndef SINGLE_THREADED
563 : 3860 : pthread_mutex_unlock(&mux_pids);
564 : : #endif
565 : :
566 : 3860 : return ret;
567 : : }
568 : 3860 : void erase (pid_t p)
569 : : {
570 : 3860 : stap_sigmasker masked;
571 : :
572 : : #ifndef SINGLE_THREADED
573 : 3860 : pthread_mutex_lock(&mux_pids);
574 : : #endif
575 [ + - ]: 3860 : pids.erase(p);
576 : : #ifndef SINGLE_THREADED
577 : 3860 : pthread_mutex_unlock(&mux_pids);
578 : : #endif
579 : 3860 : }
580 : 250 : int killall (int sig)
581 : : {
582 : 250 : int ret = 0;
583 : 250 : stap_sigmasker masked;
584 : :
585 : : #ifndef SINGLE_THREADED
586 : 250 : pthread_mutex_lock(&mux_pids);
587 : : #endif
588 [ + - ][ + + ]: 716 : for (set<pid_t>::const_iterator it = pids.begin();
589 [ + - ]: 358 : it != pids.end(); ++it)
590 [ - + ]: 108 : ret = kill(*it, sig) ?: ret;
591 : : #ifndef SINGLE_THREADED
592 : 250 : pthread_mutex_unlock(&mux_pids);
593 : : #endif
594 : 250 : return ret;
595 : : }
596 : 2938 : spawned_pids_t()
597 : 2938 : {
598 : : #ifndef SINGLE_THREADED
599 : 2419 : pthread_mutex_init(&mux_pids, NULL);
600 : : #endif
601 : 2938 : }
602 : 2938 : ~spawned_pids_t()
603 : 2938 : {
604 : : #ifndef SINGLE_THREADED
605 : 2419 : pthread_mutex_destroy (&mux_pids);
606 : : #endif
607 : 2938 : }
608 : :
609 : : };
610 : 2938 : static spawned_pids_t spawned_pids;
611 : :
612 : :
613 : : int
614 : 3860 : stap_waitpid(int verbose, pid_t pid)
615 : : {
616 : : int ret, status;
617 [ + + ][ + - ]: 3860 : if (verbose > 1 && spawned_pids.contains(pid))
[ - + ][ - + ]
618 [ # # ][ # # ]: 0 : clog << _F("Spawn waitpid call on unmanaged pid %d", pid) << endl;
[ # # ][ # # ]
619 [ + - ]: 3860 : ret = waitpid(pid, &status, 0);
620 [ + - ]: 3860 : if (ret == pid)
621 : : {
622 [ + - ]: 3860 : spawned_pids.erase(pid);
623 [ + + ]: 3860 : ret = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
624 [ + + ]: 3860 : if (verbose > 1)
625 [ + - ][ + - ]: 52 : clog << _F("Spawn waitpid result (0x%x): %d", status, ret) << endl;
[ + - ][ + - ]
626 : : }
627 : : else
628 : : {
629 [ # # ]: 0 : if (verbose > 1)
630 [ # # ][ # # ]: 0 : clog << _F("Spawn waitpid error (%d): %s", ret, strerror(errno)) << endl;
[ # # ][ # # ]
631 : 0 : ret = -1;
632 : : }
633 : 3860 : PROBE2(stap, stap_system__complete, ret, pid);
634 : 3860 : return ret;
635 : : }
636 : :
637 : : static int
638 : 10 : pipe_child_fd(posix_spawn_file_actions_t* fa, int pipefd[2], int childfd)
639 : : {
640 [ - + ]: 10 : if (pipe(pipefd))
641 : 0 : return -1;
642 : :
643 [ + + ]: 10 : int dir = childfd ? 1 : 0;
644 [ + - + - : 30 : if (!fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) &&
+ - ][ + - ]
645 : 10 : !fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) &&
646 : 10 : !posix_spawn_file_actions_adddup2(fa, pipefd[dir], childfd))
647 : 10 : return 0;
648 : :
649 : 0 : close(pipefd[0]);
650 : 0 : close(pipefd[1]);
651 : 10 : return -1;
652 : : }
653 : :
654 : : static int
655 : 22 : null_child_fd(posix_spawn_file_actions_t* fa, int childfd)
656 : : {
657 [ + - ]: 22 : int flags = childfd ? O_WRONLY : O_RDONLY;
658 : 22 : return posix_spawn_file_actions_addopen(fa, childfd, "/dev/null", flags, 0);
659 : : }
660 : :
661 : : // Runs a command with a saved PID, so we can kill it from the signal handler
662 : : pid_t
663 : 3860 : stap_spawn(int verbose, const vector<string>& args,
664 : : posix_spawn_file_actions_t* fa, const vector<string>& envVec)
665 : : {
666 : 3860 : string::const_iterator it;
667 [ + - ]: 3860 : it = args[0].begin();
668 : : const char *cmd;
669 [ + - ]: 3860 : string command;
670 [ + + ][ + - ]: 3860 : if(*it == '/' && (access(args[0].c_str(), X_OK)==-1)) //checking to see if staprun is executable
[ - + ][ - + ]
671 : : // XXX PR13274 needs-session to use print_warning()
672 [ # # ][ # # ]: 0 : clog << _F("WARNING: %s is not executable (%s)", args[0].c_str(), strerror(errno)) << endl;
[ # # ][ # # ]
[ # # ]
673 [ + + ]: 26265 : for (size_t i = 0; i < args.size(); ++i)
674 [ + - ][ + - ]: 22405 : command += " " + args[i];
[ + - ]
675 [ + - ]: 3860 : cmd = command.c_str();
676 : 3860 : PROBE1(stap, stap_system__start, cmd);
677 [ + + ]: 3860 : if (verbose > 1)
678 [ + - ][ + - ]: 52 : clog << _("Running") << command << endl;
[ + - ]
679 : :
680 : 3860 : char const * argv[args.size() + 1];
681 [ + + ]: 26265 : for (size_t i = 0; i < args.size(); ++i)
682 [ + - ]: 22405 : argv[i] = args[i].c_str();
683 : 3860 : argv[args.size()] = NULL;
684 : :
685 : : char** env;
686 : : bool allocated;
687 : : // environ can be NULL. This has been observed when running under gdb.
688 [ + - ][ + + ]: 3860 : if(envVec.empty() && environ != NULL)
[ + - ][ + + ]
689 : : {
690 : 3825 : env = environ;
691 : 3825 : allocated = false;
692 : : }
693 : : else
694 : : {
695 : 35 : allocated = true;
696 [ + - ][ + - ]: 35 : env = new char*[envVec.size() + 1];
697 : :
698 [ + + ]: 1618 : for (size_t i = 0; i < envVec.size(); ++i)
699 [ + - ]: 1583 : env[i] = (char*)envVec[i].c_str();
700 : 35 : env[envVec.size()] = NULL;
701 : : }
702 : :
703 : 3860 : pid_t pid = 0;
704 : : int ret = posix_spawnp(&pid, argv[0], fa, NULL,
705 [ + - ]: 3860 : const_cast<char * const *>(argv), env);
706 [ + + ]: 3860 : if (allocated)
707 [ + - ]: 35 : delete[] env;
708 : :
709 : 3860 : PROBE2(stap, stap_system__spawn, ret, pid);
710 [ - + ]: 3860 : if (ret != 0)
711 : : {
712 [ # # ]: 0 : if (verbose > 1)
713 [ # # ][ # # ]: 0 : clog << _F("Spawn error (%d): %s", ret, strerror(ret)) << endl;
[ # # ][ # # ]
714 : 0 : pid = -1;
715 : : }
716 : : else
717 [ + - ]: 3860 : spawned_pids.insert(pid);
718 [ + - ]: 3860 : return pid;
719 : : }
720 : :
721 : : // The API version of stap_spawn doesn't expose file_actions, for now.
722 : : pid_t
723 : 473 : stap_spawn(int verbose, const vector<string>& args)
724 : : {
725 [ + - ]: 473 : return stap_spawn(verbose, args, NULL);
726 : : }
727 : :
728 : : pid_t
729 : 6 : stap_spawn_piped(int verbose, const vector<string>& args,
730 : : int *child_in, int *child_out, int* child_err)
731 : : {
732 : 6 : pid_t pid = -1;
733 : : int infd[2], outfd[2], errfd[2];
734 : : posix_spawn_file_actions_t fa;
735 [ - + ]: 6 : if (posix_spawn_file_actions_init(&fa) != 0)
736 : 0 : return -1;
737 : :
738 [ + + ][ + - ]: 6 : if (child_in && pipe_child_fd(&fa, infd, 0) != 0)
[ - + ][ - + ]
739 : 0 : goto cleanup_fa;
740 [ + - ][ + - ]: 6 : if (child_out && pipe_child_fd(&fa, outfd, 1) != 0)
[ - + ][ - + ]
741 : 0 : goto cleanup_in;
742 [ - + ][ # # ]: 6 : if (child_err && pipe_child_fd(&fa, errfd, 2) != 0)
[ # # ][ - + ]
743 : 0 : goto cleanup_out;
744 : :
745 [ + - ][ + - ]: 6 : pid = stap_spawn(verbose, args, &fa);
[ + - ]
746 : :
747 [ - + ]: 6 : if (child_err)
748 : : {
749 [ # # ]: 0 : if (pid > 0)
750 : 0 : *child_err = errfd[0];
751 : : else
752 [ # # ]: 0 : close(errfd[0]);
753 [ # # ]: 0 : close(errfd[1]);
754 : : }
755 : :
756 : : cleanup_out:
757 [ + - ]: 6 : if (child_out)
758 : : {
759 [ + - ]: 6 : if (pid > 0)
760 : 6 : *child_out = outfd[0];
761 : : else
762 [ # # ]: 0 : close(outfd[0]);
763 [ + - ]: 6 : close(outfd[1]);
764 : : }
765 : :
766 : : cleanup_in:
767 [ + + ]: 6 : if (child_in)
768 : : {
769 [ + - ]: 4 : if (pid > 0)
770 : 4 : *child_in = infd[1];
771 : : else
772 [ # # ]: 0 : close(infd[1]);
773 [ + - ]: 4 : close(infd[0]);
774 : : }
775 : :
776 : : cleanup_fa:
777 : 6 : posix_spawn_file_actions_destroy(&fa);
778 : :
779 : 6 : return pid;
780 : : }
781 : :
782 : : // Global set of supported localization variables. Make changes here to
783 : : // add or remove variables. List of variables from:
784 : : // http://publib.boulder.ibm.com/infocenter/tivihelp/v8r1/index.jsp?topic=/
785 : : // com.ibm.netcool_OMNIbus.doc_7.3.0/omnibus/wip/install/concept/omn_con_settingyourlocale.html
786 : : const set<string>&
787 : 70 : localization_variables()
788 : : {
789 [ + + ][ + - ]: 70 : static set<string> localeVars;
[ + - ][ # # ]
790 [ + + ]: 70 : if (localeVars.empty())
791 : : {
792 [ + - ][ + - ]: 39 : localeVars.insert("LANG");
[ + - ]
793 [ + - ][ + - ]: 39 : localeVars.insert("LC_ALL");
[ + - ]
794 [ + - ][ + - ]: 39 : localeVars.insert("LC_CTYPE");
[ + - ]
795 [ + - ][ + - ]: 39 : localeVars.insert("LC_COLLATE");
[ + - ]
796 [ + - ][ + - ]: 39 : localeVars.insert("LC_MESSAGES");
[ + - ]
797 [ + - ][ + - ]: 39 : localeVars.insert("LC_TIME");
[ + - ]
798 [ + - ][ + - ]: 39 : localeVars.insert("LC_MONETARY");
[ + - ]
799 [ + - ][ + - ]: 39 : localeVars.insert("LC_NUMERIC");
[ + - ]
800 : : }
801 : 70 : return localeVars;
802 : : }
803 : :
804 : : // Runs a command with a saved PID, so we can kill it from the signal handler,
805 : : // and wait for it to finish.
806 : : int
807 : 3311 : stap_system(int verbose, const string& description,
808 : : const vector<string>& args,
809 : : bool null_out, bool null_err)
810 : : {
811 : 3311 : int ret = 0;
812 : : posix_spawn_file_actions_t fa;
813 [ - + ]: 3311 : if (posix_spawn_file_actions_init(&fa) != 0)
814 : 0 : return -1;
815 : :
816 [ + + ][ + - ]: 3322 : if ((null_out && null_child_fd(&fa, 1) != 0) ||
[ + + - + ]
[ - + ]
817 : 11 : (null_err && null_child_fd(&fa, 2) != 0))
818 : 0 : ret = -1;
819 : : else
820 : : {
821 [ + - ][ + - ]: 3311 : pid_t pid = stap_spawn(verbose, args, &fa);
[ + - ]
822 : 3311 : ret = pid;
823 [ + - ]: 3311 : if (pid > 0){
824 [ + - ]: 3311 : ret = stap_waitpid(verbose, pid);
825 : :
826 : : // XXX PR13274 needs-session to use print_warning()
827 [ + + ]: 3311 : if (ret > 128)
828 [ + - ][ + - ]: 2 : clog << _F("WARNING: %s exited with signal: %d (%s)",
[ + - ][ + - ]
829 [ + - ]: 1 : description.c_str(), ret - 128, strsignal(ret - 128)) << endl;
830 [ + + ]: 3310 : else if (ret > 0)
831 [ + - ][ + - ]: 10 : clog << _F("WARNING: %s exited with status: %d",
[ + - ][ + - ]
832 [ + - ]: 5 : description.c_str(), ret) << endl;
833 : : }
834 : : }
835 : :
836 : 3311 : posix_spawn_file_actions_destroy(&fa);
837 : 3311 : return ret;
838 : : }
839 : :
840 : : // Like stap_system, but capture stdout
841 : : int
842 : 0 : stap_system_read(int verbose, const vector<string>& args, ostream& out)
843 : : {
844 : 0 : int child_fd = -1;
845 [ # # ]: 0 : pid_t child = stap_spawn_piped(verbose, args, NULL, &child_fd);
846 [ # # ]: 0 : if (child > 0)
847 : : {
848 : : // read everything from the child
849 [ # # ]: 0 : stdio_filebuf<char> in(child_fd, ios_base::in);
850 [ # # ]: 0 : out << ∈
851 [ # # ][ # # ]: 0 : return stap_waitpid(verbose, child);
852 : : }
853 : 0 : return -1;
854 : : }
855 : :
856 : :
857 : : // Send a signal to our spawned commands
858 : : int
859 : 250 : kill_stap_spawn(int sig)
860 : : {
861 : 250 : return spawned_pids.killall(sig);
862 : : }
863 : :
864 : :
865 : :
866 : 2707 : void assert_regexp_match (const string& name, const string& value, const string& re)
867 : : {
868 : : typedef map<string,regex_t*> cache;
869 [ + + ][ + - ]: 2707 : static cache compiled;
[ + - ][ # # ]
870 [ + - ]: 2707 : cache::iterator it = compiled.find (re);
871 : 2707 : regex_t* r = 0;
872 [ + - ][ + + ]: 2707 : if (it == compiled.end())
873 : : {
874 [ + - ]: 2691 : r = new regex_t;
875 [ + - ][ + - ]: 2691 : int rc = regcomp (r, re.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED);
876 [ - + ]: 2691 : assert (rc == 0);
877 [ + - ]: 2691 : compiled[re] = r;
878 : : }
879 : : else
880 [ + - ]: 16 : r = it->second;
881 : :
882 : : // run regexec
883 [ + - ][ + - ]: 2707 : int rc = regexec (r, value.c_str(), 0, 0, 0);
884 [ + + ]: 2707 : if (rc)
885 : : throw runtime_error
886 : 62 : (_F("ERROR: Safety pattern mismatch for %s ('%s' vs. '%s') rc=%d",
887 [ + - ][ + - ]: 62 : name.c_str(), value.c_str(), re.c_str(), rc));
[ + - ][ + - ]
[ + - ]
888 : 2645 : }
889 : :
890 : :
891 : 2357 : int regexp_match (const string& value, const string& re, vector<string>& matches)
892 : : {
893 : : typedef map<string,regex_t*> cache; // separate cache because we use different regcomp options
894 [ + + ][ + - ]: 2357 : static cache compiled;
[ + - ][ # # ]
895 [ + - ]: 2357 : cache::iterator it = compiled.find (re);
896 : 2357 : regex_t* r = 0;
897 [ + - ][ + + ]: 2357 : if (it == compiled.end())
898 : : {
899 [ + - ]: 2224 : r = new regex_t;
900 [ + - ][ + - ]: 2224 : int rc = regcomp (r, re.c_str(), REG_EXTENDED); /* REG_ICASE? */
901 [ - + ]: 2224 : assert (rc == 0);
902 [ + - ]: 2224 : compiled[re] = r;
903 : : }
904 : : else
905 [ + - ]: 133 : r = it->second;
906 : :
907 : :
908 : : // run regexec
909 : : #define maxmatches 10
910 : : regmatch_t rm[maxmatches];
911 : :
912 [ + - ][ + - ]: 2357 : int rc = regexec (r, value.c_str(), maxmatches, rm, 0);
913 [ + + ]: 2357 : if (rc) return rc;
914 : :
915 [ + - ][ + - ]: 2273 : matches.erase(matches.begin(), matches.end());
[ + - ]
916 [ + + ]: 25003 : for (unsigned i=0; i<maxmatches; i++) // XXX: ideally, the number of actual subexpressions in re
917 : : {
918 [ + + ]: 22730 : if (rm[i].rm_so >= 0)
919 [ + - ][ + - ]: 6785 : matches.push_back(value.substr (rm[i].rm_so, rm[i].rm_eo-rm[i].rm_so));
[ + - ]
920 : : else
921 [ + - ][ + - ]: 15945 : matches.push_back("");
[ + - ]
922 : : }
923 : :
924 : 2357 : return 0;
925 : : }
926 : :
927 : :
928 : 115 : bool contains_glob_chars (const string& str)
929 : : {
930 [ + + ]: 2298 : for (unsigned i=0; i<str.size(); i++)
931 : : {
932 : 2198 : char this_char = str[i];
933 [ + + ][ + - ]: 2198 : if (this_char == '\\' && (str.size() > i+1))
[ + + ]
934 : : {
935 : : // PR13338: skip the escape backslash and the escaped character
936 : 1 : i++;
937 : 1 : continue;
938 : : }
939 [ + + ][ + + ]: 2197 : if (this_char == '*' || this_char == '?' || this_char == '[')
[ - + ]
940 : 15 : return true;
941 : : }
942 : :
943 : 115 : return false;
944 : : }
945 : :
946 : :
947 : : // PR13338: we need these functions to be able to pass through glob metacharacters
948 : : // through the recursive process("...*...") expansion process.
949 : 14 : string escape_glob_chars (const string& str)
950 : : {
951 : 14 : string op;
952 [ + - ][ + + ]: 302 : for (unsigned i=0; i<str.size(); i++)
953 : : {
954 [ + - ]: 288 : char this_char = str[i];
955 [ + - ][ + - ]: 288 : if (this_char == '*' || this_char == '?' || this_char == '[')
[ + + ]
956 [ + - ]: 1 : op += '\\';
957 [ + - ]: 288 : op += this_char;
958 : : }
959 : 14 : return op;
960 : : }
961 : :
962 : 67 : string unescape_glob_chars (const string& str)
963 : : {
964 : 67 : string op;
965 [ + - ][ + + ]: 1538 : for (unsigned i=0; i<str.size(); i++)
966 : : {
967 [ + - ]: 1471 : char this_char = str[i];
968 [ + + ][ + - ]: 1471 : if (this_char == '\\' && (str.size() > i+1) )
[ + - ][ + + ]
969 : : {
970 [ + - ][ + - ]: 1 : op += str[i+1];
971 : 1 : i++;
972 : 1 : continue;
973 : : }
974 [ + - ]: 1470 : op += this_char;
975 : : }
976 : :
977 : 67 : return op;
978 : : }
979 : :
980 : :
981 : :
982 : : string
983 : 2423 : normalize_machine(const string& machine)
984 : : {
985 : : // PR4186: Copy logic from coreutils uname (uname -i) to squash
986 : : // i?86->i386. Actually, copy logic from linux top-level Makefile
987 : : // to squash uname -m -> $(SUBARCH).
988 : : //
989 : : // This logic needs to match the logic in the stap_get_arch shell
990 : : // function in stap-env.
991 : : //
992 : : // But: RHBZ669082 reminds us that this renaming post-dates some
993 : : // of the kernel versions we know and love. So in buildrun.cxx
994 : : // we undo this renaming for ancient powerpc.
995 : :
996 [ - + ][ # # ]: 2423 : if (machine == "i486") return "i386";
997 [ - + ][ # # ]: 2423 : else if (machine == "i586") return "i386";
998 [ - + ][ # # ]: 2423 : else if (machine == "i686") return "i386";
999 [ - + ][ # # ]: 2423 : else if (machine == "sun4u") return "sparc64";
1000 [ + - ][ + - ]: 2423 : else if (machine.substr(0,3) == "arm") return "arm";
[ + - ]
1001 [ # # ][ # # ]: 0 : else if (machine == "sa110") return "arm";
1002 [ # # ][ # # ]: 0 : else if (machine == "s390x") return "s390";
1003 [ # # ][ # # ]: 0 : else if (machine.substr(0,3) == "ppc") return "powerpc";
[ # # ]
1004 [ # # ][ # # ]: 0 : else if (machine.substr(0,4) == "mips") return "mips";
[ # # ]
1005 [ # # ][ # # ]: 0 : else if (machine.substr(0,3) == "sh2") return "sh";
[ # # ]
1006 [ # # ][ # # ]: 0 : else if (machine.substr(0,3) == "sh3") return "sh";
[ # # ]
1007 [ # # ][ # # ]: 0 : else if (machine.substr(0,3) == "sh4") return "sh";
[ # # ]
1008 : 2423 : return machine;
1009 : : }
1010 : :
1011 : : int
1012 : 69 : elf_class_from_normalized_machine (const string &machine)
1013 : : {
1014 : : // Must match kernel machine architectures as used un tapset directory.
1015 : : // And must match normalization done in normalize_machine ().
1016 [ + - + - ]: 138 : if (machine == "i386"
[ + - ]
1017 : 69 : || machine == "arm") // arm assumes 32-bit
1018 : 69 : return ELFCLASS32;
1019 [ # # # # : 0 : else if (machine == "s390" // powerpc and s390 always assume 64-bit,
# # # # ]
[ # # ]
1020 : 0 : || machine == "powerpc" // see normalize_machine ().
1021 : 0 : || machine == "x86_64"
1022 : 0 : || machine == "ia64")
1023 : 0 : return ELFCLASS64;
1024 : :
1025 [ # # ]: 0 : cerr << _F("Unknown kernel machine architecture '%s', don't know elf class",
1026 [ # # ]: 0 : machine.c_str()) << endl;
1027 : 69 : return -1;
1028 : : }
1029 : :
1030 : : string
1031 : 0 : kernel_release_from_build_tree (const string &kernel_build_tree, int verbose)
1032 : : {
1033 [ # # ]: 0 : string version_file_name = kernel_build_tree + "/include/config/kernel.release";
1034 : : // The file include/config/kernel.release within the
1035 : : // build tree is used to pull out the version information
1036 [ # # ][ # # ]: 0 : ifstream version_file (version_file_name.c_str());
1037 [ # # ][ # # ]: 0 : if (version_file.fail ())
1038 : : {
1039 [ # # ]: 0 : if (verbose > 1)
1040 : : //TRANSLATORS: Missing a file
1041 [ # # ][ # # ]: 0 : cerr << _F("Missing %s", version_file_name.c_str()) << endl;
[ # # ][ # # ]
[ # # ]
1042 [ # # ]: 0 : return "";
1043 : : }
1044 : :
1045 [ # # ]: 0 : string kernel_release;
1046 : : char c;
1047 [ # # ][ # # ]: 0 : while (version_file.get(c) && c != '\n')
[ # # ][ # # ]
[ # # ]
1048 [ # # ]: 0 : kernel_release.push_back(c);
1049 : :
1050 [ # # ][ # # ]: 0 : return kernel_release;
[ # # ][ # # ]
1051 : : }
1052 : :
1053 : 380699 : std::string autosprintf(const char* format, ...)
1054 : : {
1055 : : va_list args;
1056 : : char *str;
1057 : 380699 : va_start (args, format);
1058 : 380699 : int rc = vasprintf (&str, format, args);
1059 [ - + ]: 380699 : if (rc < 0)
1060 : : {
1061 : 0 : va_end(args);
1062 [ # # ]: 0 : return _F("autosprintf/vasprintf error %d", rc);
1063 : : }
1064 [ + - ]: 380699 : string s = str;
1065 : 380699 : va_end (args);
1066 : 380699 : free (str);
1067 [ + - ][ + - ]: 380699 : return s; /* by copy */
1068 : : }
1069 : :
1070 : : std::string
1071 : 880 : get_self_path()
1072 : : {
1073 : : char buf[1024]; // This really should be enough for anybody...
1074 : 880 : const char *file = "/proc/self/exe";
1075 : 880 : ssize_t len = readlink(file, buf, sizeof(buf) - 1);
1076 [ + - ]: 880 : if (len > 0)
1077 : : {
1078 : 880 : buf[len] = '\0';
1079 : 880 : file = buf;
1080 : : }
1081 : : // otherwise the path is ridiculously large, fall back to /proc/self/exe.
1082 : : //
1083 [ + - ]: 880 : return std::string(file);
1084 [ + - ][ + - ]: 8814 : }
1085 : :
1086 : :
1087 : : #ifndef HAVE_PPOLL
1088 : : // This is a poor-man's ppoll, only used carefully by readers that need to be
1089 : : // interruptible, like remote::run and mutator::run. It does not provide the
1090 : : // same guarantee of atomicity as on systems with a true ppoll.
1091 : : //
1092 : : // In our use, this would cause trouble if a signal came in any time from the
1093 : : // moment we mask signals to prepare pollfds, to the moment we call poll in
1094 : : // emulation here. If there's no data on any of the pollfds, we will be stuck
1095 : : // waiting indefinitely.
1096 : : //
1097 : : // Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
1098 : : // throw in a one-second forced timeout to ensure we have a chance to notice
1099 : : // there was an interrupt without too much delay.
1100 : : int
1101 : : ppoll(struct pollfd *fds, nfds_t nfds,
1102 : : const struct timespec *timeout_ts,
1103 : : const sigset_t *sigmask)
1104 : : {
1105 : : sigset_t origmask;
1106 : : int timeout = (timeout_ts == NULL) ? 1000 // don't block forever...
1107 : : : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
1108 : : sigprocmask(SIG_SETMASK, sigmask, &origmask);
1109 : : int rc = poll(fds, nfds, timeout);
1110 : : sigprocmask(SIG_SETMASK, &origmask, NULL);
1111 : : return rc;
1112 : : }
1113 : : #endif
1114 : :
1115 : :
1116 : : /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
|