]> sourceware.org Git - systemtap.git/blame - util.cxx
Add am64 support
[systemtap.git] / util.cxx
CommitLineData
1b78aef5 1// Copyright (C) Andrew Tridgell 2002 (original file)
7a1513b1 2// Copyright (C) 2006-2011 Red Hat Inc. (systemtap changes)
1b78aef5
DS
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
e8daaf60 15// along with this program. If not, see <http://www.gnu.org/licenses/>.
1b78aef5
DS
16
17#include "util.h"
0f5d597d 18#include "stap-probe.h"
1b78aef5
DS
19#include <stdexcept>
20#include <cerrno>
c0d1b5a0 21#include <map>
01cc94dc 22#include <set>
c0d1b5a0 23#include <string>
85007c04 24#include <fstream>
3b6f3bbb 25#include <cassert>
77455f97 26#include <ext/stdio_filebuf.h>
1b78aef5
DS
27
28extern "C" {
081b45d1 29#include <elf.h>
4cc40e82 30#include <fcntl.h>
baba4e15 31#include <grp.h>
1b78aef5 32#include <pwd.h>
4cc40e82 33#include <spawn.h>
8bf4a144 34#include <stdio.h>
1b78aef5 35#include <stdlib.h>
4cc40e82
JS
36#include <sys/stat.h>
37#include <sys/types.h>
38#include <sys/wait.h>
39#include <unistd.h>
c0d1b5a0 40#include <regex.h>
7a1513b1 41#include <stdarg.h>
83ae46dd 42#ifndef SINGLE_THREADED
26a39006 43#include <pthread.h>
83ae46dd 44#endif
1b78aef5
DS
45}
46
47using namespace std;
77455f97 48using namespace __gnu_cxx;
1b78aef5
DS
49
50
51// Return current users home directory or die.
52const char *
53get_home_directory(void)
54{
55 const char *p = getenv("HOME");
56 if (p)
57 return p;
58
59 struct passwd *pwd = getpwuid(getuid());
60 if (pwd)
61 return pwd->pw_dir;
62
35f29567
CM
63 cerr << _("Unable to determine home directory") << endl;
64 return "/";
1b78aef5
DS
65}
66
67
b12c8986
DB
68// Get the size of a file in bytes
69size_t
70get_file_size(const string &path)
71{
72 struct stat file_info;
73
74 if (stat(path.c_str(), &file_info) == 0)
75 return file_info.st_size;
76 else
77 return 0;
78}
79
80// Get the size of a file in bytes
a5751672
JS
81size_t
82get_file_size(int fd)
83{
84 struct stat file_info;
85
86 if (fstat(fd, &file_info) == 0)
87 return file_info.st_size;
88 else
89 return 0;
90}
91
92// Check that a file is present
b12c8986
DB
93bool
94file_exists (const string &path)
95{
96 struct stat file_info;
97
98 if (stat(path.c_str(), &file_info) == 0)
99 return true;
100
101 return false;
102}
103
1b78aef5
DS
104// Copy a file. The copy is done via a temporary file and atomic
105// rename.
e16dc041
JS
106bool
107copy_file(const string& src, const string& dest, bool verbose)
1b78aef5
DS
108{
109 int fd1, fd2;
110 char buf[10240];
111 int n;
112 string tmp;
113 char *tmp_name;
114 mode_t mask;
115
e16dc041 116 if (verbose)
b530b5b3 117 clog << _F("Copying %s to %s", src.c_str(), dest.c_str()) << endl;
e16dc041 118
1b78aef5 119 // Open the src file.
e16dc041 120 fd1 = open(src.c_str(), O_RDONLY);
1b78aef5 121 if (fd1 == -1)
e16dc041 122 goto error;
1b78aef5
DS
123
124 // Open the temporary output file.
125 tmp = dest + string(".XXXXXX");
126 tmp_name = (char *)tmp.c_str();
127 fd2 = mkstemp(tmp_name);
128 if (fd2 == -1)
129 {
130 close(fd1);
e16dc041 131 goto error;
1b78aef5
DS
132 }
133
134 // Copy the src file to the temporary output file.
135 while ((n = read(fd1, buf, sizeof(buf))) > 0)
136 {
137 if (write(fd2, buf, n) != n)
138 {
139 close(fd2);
140 close(fd1);
141 unlink(tmp_name);
e16dc041 142 goto error;
1b78aef5
DS
143 }
144 }
145 close(fd1);
146
147 // Set the permissions on the temporary output file.
148 mask = umask(0);
149 fchmod(fd2, 0666 & ~mask);
150 umask(mask);
151
152 // Close the temporary output file. The close can fail on NFS if
153 // out of space.
154 if (close(fd2) == -1)
155 {
156 unlink(tmp_name);
e16dc041 157 goto error;
1b78aef5
DS
158 }
159
160 // Rename the temporary output file to the destination file.
e16dc041
JS
161 unlink(dest.c_str());
162 if (rename(tmp_name, dest.c_str()) == -1)
1b78aef5
DS
163 {
164 unlink(tmp_name);
e16dc041 165 goto error;
1b78aef5
DS
166 }
167
e16dc041
JS
168 return true;
169
170error:
b530b5b3
LB
171 cerr << _F("Copy failed (\"%s\" to \"%s\"): %s", src.c_str(),
172 dest.c_str(), strerror(errno)) << endl;
e16dc041 173 return false;
1b78aef5
DS
174}
175
176
177// Make sure a directory exists.
178int
3b6f3bbb 179create_dir(const char *dir, int mode)
1b78aef5
DS
180{
181 struct stat st;
182 if (stat(dir, &st) == 0)
183 {
184 if (S_ISDIR(st.st_mode))
185 return 0;
186 errno = ENOTDIR;
187 return 1;
188 }
189
3b6f3bbb
DB
190 // Create the directory. We must create each component
191 // of the path ourselves.
192 vector<string> components;
193 tokenize (dir, components, "/");
194 string path;
195 if (*dir == '/')
196 {
197 // Absolute path
198 path = "/";
199 }
200 unsigned limit = components.size ();
201 assert (limit != 0);
202 for (unsigned ix = 0; ix < limit; ++ix)
203 {
204 path += components[ix] + '/';
205 if (mkdir(path.c_str (), mode) != 0 && errno != EEXIST)
206 return 1;
207 }
1b78aef5
DS
208
209 return 0;
210}
211
98f552c2
DB
212// Remove a file or directory
213int
214remove_file_or_dir (const char *name)
215{
216 int rc;
217 struct stat st;
218
219 if ((rc = stat(name, &st)) != 0)
220 {
221 if (errno == ENOENT)
222 return 0;
223 return 1;
224 }
225
226 if (remove (name) != 0)
227 return 1;
aeb9cc10 228
98f552c2
DB
229 return 0;
230}
1b78aef5 231
3892d516
DB
232/* Obtain the gid of the given group. */
233gid_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 stgr = getgrnam(group_name);
238 if (stgr == NULL)
239 return (gid_t)-1;
240 return stgr->gr_gid;
241}
242
0da3e7a0
DB
243// Determine whether the current user is in the given group
244// by gid.
baba4e15 245bool
0da3e7a0 246in_group_id (gid_t target_gid)
baba4e15 247{
baba4e15
DB
248 // According to the getgroups() man page, getgroups() may not
249 // return the effective gid, so try to match it first. */
db0a43c3 250 if (target_gid == getegid())
baba4e15
DB
251 return true;
252
253 // Get the list of the user's groups.
f429df66 254 int ngids = getgroups(0, 0); // Returns the number to allocate.
db0a43c3
RM
255 if (ngids > 0) {
256 gid_t gidlist[ngids];
257 ngids = getgroups(ngids, gidlist);
258 for (int i = 0; i < ngids; i++) {
259 // If the user is a member of the target group, then we're done.
260 if (gidlist[i] == target_gid)
261 return true;
262 }
263 }
baba4e15 264 if (ngids < 0) {
b530b5b3 265 cerr << _("Unable to retrieve group list") << endl;
baba4e15
DB
266 return false;
267 }
268
baba4e15
DB
269 // The user is not a member of the target group
270 return false;
271}
272
85007c04
DB
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 */
278string
279getmemusage ()
280{
281 static long sz = sysconf(_SC_PAGESIZE);
282
d54a65f6 283 long pages;
85007c04
DB
284 ostringstream oss;
285 ifstream statm("/proc/self/statm");
286 statm >> pages;
c338e2c5 287 long kb1 = pages * sz / 1024; // total program size; vmsize
85007c04 288 statm >> pages;
c338e2c5 289 long kb2 = pages * sz / 1024; // resident set size; vmrss
85007c04 290 statm >> pages;
c338e2c5
FCE
291 long kb3 = pages * sz / 1024; // shared pages
292 statm >> pages;
293 long kb4 = pages * sz / 1024; // text
294 statm >> pages;
295 (void) kb4;
296 long kb5 = pages * sz / 1024; // library
297 statm >> pages;
298 (void) kb5;
299 long kb6 = pages * sz / 1024; // data+stack
300 statm >> pages;
301 long kb7 = pages * sz / 1024; // dirty
302 (void) kb7;
303
304 oss << _F("using %ldvirt/%ldres/%ldshr/%lddata kb, ", kb1, kb2, kb3, kb6);
85007c04
DB
305 return oss.str();
306}
307
1b78aef5
DS
308void
309tokenize(const string& str, vector<string>& tokens,
310 const string& delimiters = " ")
311{
312 // Skip delimiters at beginning.
313 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
314 // Find first "non-delimiter".
315 string::size_type pos = str.find_first_of(delimiters, lastPos);
316
317 while (pos != string::npos || lastPos != string::npos)
318 {
319 // Found a token, add it to the vector.
320 tokens.push_back(str.substr(lastPos, pos - lastPos));
321 // Skip delimiters. Note the "not_of"
322 lastPos = str.find_first_not_of(delimiters, pos);
323 // Find next "non-delimiter"
324 pos = str.find_first_of(delimiters, lastPos);
325 }
326}
327
49dbe419
DB
328// Akin to tokenize(...,...), but allow tokens before the first delimeter, after the
329// last delimiter and allow internal empty tokens
330void
331tokenize_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 if (str.size() <= 1)
337 return;
338
339 // Find the first delimeter.
340 string::size_type lastPos = 0;
341 string::size_type pos = str.find_first_of(delimiters, lastPos);
342 if (pos == string::npos)
343 return; // no delimeters
344
345 /* No leading empty component allowed. */
346 if (pos == lastPos)
347 ++lastPos;
348
349 assert (lastPos < str.size());
350 do
351 {
352 pos = str.find_first_of(delimiters, lastPos);
353 if (pos == string::npos)
354 break; // Final trailing component
355 // Found a token, add it to the vector.
356 tokens.push_back(str.substr (lastPos, pos - lastPos));
357 // Skip the delimiter.
358 lastPos = pos + 1;
359 }
360 while (lastPos < str.size());
361
362 // A final non-delimited token, if it is not empty.
363 if (lastPos < str.size())
364 {
365 assert (pos == string::npos);
366 tokens.push_back(str.substr (lastPos));
367 }
368}
1b78aef5 369
91699a70
JS
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.
373void
374tokenize_cxx(const string& str, vector<string>& tokens)
375{
376 int angle_count = 0;
377 string::size_type pos = 0;
378 string::size_type colon_pos = str.find("::");
379 string::size_type angle_pos = str.find_first_of("<>");
380 while (colon_pos != string::npos &&
381 (angle_count == 0 || angle_pos != string::npos))
382 {
383 if (angle_count > 0 || angle_pos < colon_pos)
384 {
385 angle_count += str.at(angle_pos) == '<' ? 1 : -1;
386 colon_pos = str.find("::", angle_pos + 1);
387 angle_pos = str.find_first_of("<>", angle_pos + 1);
388 }
389 else
390 {
391 tokens.push_back(str.substr(pos, colon_pos - pos));
392 pos = colon_pos + 2;
393 colon_pos = str.find("::", pos);
394 angle_pos = str.find_first_of("<>", pos);
395 }
396 }
397 tokens.push_back(str.substr(pos));
398}
399
400
d0a7f5a9
FCE
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
0a567f6d
JS
405string find_executable(const string& name)
406{
407 const map<string, string> sysenv;
408 return find_executable(name, "", sysenv);
409}
410
05fb3e0c 411string find_executable(const string& name, const string& sysroot,
0a567f6d 412 const map<string, string>& sysenv,
05fb3e0c 413 const string& env_path)
1b78aef5 414{
d9736de1 415 string retpath;
1b78aef5 416
d9736de1
FCE
417 if (name.size() == 0)
418 return name;
1b78aef5 419
d0a7f5a9
FCE
420 struct stat st;
421
422 if (name.find('/') != string::npos) // slash in the path already?
423 {
05fb3e0c 424 retpath = sysroot + name;
d0a7f5a9
FCE
425 }
426 else // Nope, search $PATH.
1b78aef5 427 {
0a567f6d 428 const char *path;
05fb3e0c 429 if (sysenv.count(env_path) != 0)
0a567f6d 430 path = sysenv.find(env_path)->second.c_str();
05fb3e0c
WF
431 else
432 path = getenv(env_path.c_str());
d9736de1 433 if (path)
1b78aef5 434 {
d9736de1
FCE
435 // Split PATH up.
436 vector<string> dirs;
437 tokenize(string(path), dirs, string(":"));
30f926f0 438
d9736de1
FCE
439 // Search the path looking for the first executable of the right name.
440 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
441 {
05fb3e0c 442 string fname = sysroot + *i + "/" + name;
d9736de1 443 const char *f = fname.c_str();
30f926f0 444
d9736de1
FCE
445 // Look for a normal executable file.
446 if (access(f, X_OK) == 0
d0a7f5a9
FCE
447 && stat(f, &st) == 0
448 && S_ISREG(st.st_mode))
d9736de1
FCE
449 {
450 retpath = fname;
451 break;
452 }
453 }
454 }
1b78aef5
DS
455 }
456
dff50e09 457
d0a7f5a9
FCE
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 if (retpath == "")
05fb3e0c 461 retpath = sysroot + name;
d0a7f5a9 462
d9736de1
FCE
463 // Canonicalize the path name.
464 char *cf = canonicalize_file_name (retpath.c_str());
30f926f0 465 if (cf)
1b78aef5 466 {
05fb3e0c
WF
467 string scf = string(cf);
468 if (sysroot.empty())
469 retpath = scf;
470 else {
471 int pos = scf.find(sysroot);
472 if (pos == 0)
473 retpath = scf;
474 else
475 throw runtime_error(_F("find_executable(): file %s not in sysroot %s", cf, sysroot.c_str()));
476 }
d9736de1
FCE
477 free (cf);
478 }
479
480 return retpath;
1b78aef5 481}
8c711d30 482
d9736de1
FCE
483
484
8c711d30
MH
485const 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 string quoted_cmd;
497 string quote("'");
498 string replace("'\"'\"'");
499 string::size_type pos = 0;
500
501 quoted_cmd += quote;
dff50e09
FCE
502 for (string::size_type quote_pos = cmd.find(quote, pos);
503 quote_pos != string::npos;
8c711d30
MH
504 quote_pos = cmd.find(quote, pos)) {
505 quoted_cmd += cmd.substr(pos, quote_pos - pos);
506 quoted_cmd += replace;
507 pos = quote_pos + 1;
508 }
509 quoted_cmd += cmd.substr(pos, cmd.length() - pos);
510 quoted_cmd += quote;
511
512 return quoted_cmd;
513}
514
a5e8d632 515
5eea6ed1
JS
516const string
517cmdstr_join(const vector<string>& cmds)
518{
519 if (cmds.empty())
b530b5b3 520 throw runtime_error(_("cmdstr_join called with an empty command!"));
5eea6ed1
JS
521
522 stringstream cmd;
523 cmd << cmdstr_quoted(cmds[0]);
524 for (size_t i = 1; i < cmds.size(); ++i)
525 cmd << " " << cmdstr_quoted(cmds[i]);
526
527 return cmd.str();
528}
529
530
b30895fa
JS
531// signal-safe set of pids
532class spawned_pids_t {
533 private:
534 set<pid_t> pids;
83ae46dd 535#ifndef SINGLE_THREADED
26a39006 536 pthread_mutex_t mux_pids;
83ae46dd 537#endif
4cc40e82 538
b30895fa
JS
539 public:
540 bool contains (pid_t p)
541 {
542 stap_sigmasker masked;
26a39006 543
83ae46dd 544#ifndef SINGLE_THREADED
26a39006 545 pthread_mutex_lock(&mux_pids);
83ae46dd 546#endif
26a39006 547 bool ret = (pids.count(p)==0) ? true : false;
83ae46dd 548#ifndef SINGLE_THREADED
26a39006 549 pthread_mutex_unlock(&mux_pids);
83ae46dd 550#endif
26a39006
CM
551
552 return ret;
b30895fa
JS
553 }
554 bool insert (pid_t p)
555 {
556 stap_sigmasker masked;
26a39006 557
83ae46dd 558#ifndef SINGLE_THREADED
26a39006 559 pthread_mutex_lock(&mux_pids);
83ae46dd 560#endif
26a39006 561 bool ret = (p > 0) ? pids.insert(p).second : false;
83ae46dd 562#ifndef SINGLE_THREADED
26a39006 563 pthread_mutex_unlock(&mux_pids);
83ae46dd 564#endif
26a39006
CM
565
566 return ret;
b30895fa
JS
567 }
568 void erase (pid_t p)
569 {
570 stap_sigmasker masked;
26a39006 571
83ae46dd 572#ifndef SINGLE_THREADED
26a39006 573 pthread_mutex_lock(&mux_pids);
83ae46dd 574#endif
b30895fa 575 pids.erase(p);
83ae46dd 576#ifndef SINGLE_THREADED
26a39006 577 pthread_mutex_unlock(&mux_pids);
83ae46dd 578#endif
b30895fa
JS
579 }
580 int killall (int sig)
581 {
582 int ret = 0;
583 stap_sigmasker masked;
26a39006 584
83ae46dd 585#ifndef SINGLE_THREADED
26a39006 586 pthread_mutex_lock(&mux_pids);
83ae46dd 587#endif
a5fa9c25
JS
588 for (set<pid_t>::const_iterator it = pids.begin();
589 it != pids.end(); ++it)
b30895fa 590 ret = kill(*it, sig) ?: ret;
83ae46dd 591#ifndef SINGLE_THREADED
26a39006 592 pthread_mutex_unlock(&mux_pids);
83ae46dd 593#endif
b30895fa
JS
594 return ret;
595 }
26a39006
CM
596 spawned_pids_t()
597 {
83ae46dd 598#ifndef SINGLE_THREADED
26a39006 599 pthread_mutex_init(&mux_pids, NULL);
83ae46dd 600#endif
26a39006
CM
601 }
602 ~spawned_pids_t()
603 {
83ae46dd 604#ifndef SINGLE_THREADED
26a39006 605 pthread_mutex_destroy (&mux_pids);
83ae46dd 606#endif
26a39006
CM
607 }
608
b30895fa
JS
609};
610static spawned_pids_t spawned_pids;
4cc40e82 611
01cc94dc
JS
612
613int
614stap_waitpid(int verbose, pid_t pid)
daa75206
JS
615{
616 int ret, status;
b30895fa
JS
617 if (verbose > 1 && spawned_pids.contains(pid))
618 clog << _F("Spawn waitpid call on unmanaged pid %d", pid) << endl;
01cc94dc
JS
619 ret = waitpid(pid, &status, 0);
620 if (ret == pid)
daa75206 621 {
01cc94dc 622 spawned_pids.erase(pid);
daa75206 623 ret = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
483cf56e 624 if (verbose > 1)
b530b5b3 625 clog << _F("Spawn waitpid result (0x%x): %d", status, ret) << endl;
daa75206
JS
626 }
627 else
628 {
629 if (verbose > 1)
b530b5b3 630 clog << _F("Spawn waitpid error (%d): %s", ret, strerror(errno)) << endl;
daa75206
JS
631 ret = -1;
632 }
20f90026 633 PROBE2(stap, stap_system__complete, ret, pid);
daa75206
JS
634 return ret;
635}
636
b63fab87
JS
637static int
638pipe_child_fd(posix_spawn_file_actions_t* fa, int pipefd[2], int childfd)
639{
640 if (pipe(pipefd))
641 return -1;
642
643 int dir = childfd ? 1 : 0;
644 if (!fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) &&
645 !fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) &&
646 !posix_spawn_file_actions_adddup2(fa, pipefd[dir], childfd))
647 return 0;
648
649 close(pipefd[0]);
650 close(pipefd[1]);
651 return -1;
652}
653
ff520ff4
JS
654static int
655null_child_fd(posix_spawn_file_actions_t* fa, int childfd)
656{
657 int flags = childfd ? O_WRONLY : O_RDONLY;
658 return posix_spawn_file_actions_addopen(fa, childfd, "/dev/null", flags, 0);
659}
660
4cc40e82 661// Runs a command with a saved PID, so we can kill it from the signal handler
aeb9cc10 662pid_t
20f90026 663stap_spawn(int verbose, const vector<string>& args,
e4e3d6b7 664 posix_spawn_file_actions_t* fa, const vector<string>& envVec)
4cc40e82 665{
5def5d2a
LB
666 string::const_iterator it;
667 it = args[0].begin();
20f90026
JS
668 const char *cmd;
669 string command;
5def5d2a 670 if(*it == '/' && (access(args[0].c_str(), X_OK)==-1)) //checking to see if staprun is executable
2713ea24
CM
671 // XXX PR13274 needs-session to use print_warning()
672 clog << _F("WARNING: %s is not executable (%s)", args[0].c_str(), strerror(errno)) << endl;
20f90026
JS
673 for (size_t i = 0; i < args.size(); ++i)
674 command += " " + args[i];
675 cmd = command.c_str();
676 PROBE1(stap, stap_system__start, cmd);
db0a43c3 677 if (verbose > 1)
b530b5b3 678 clog << _("Running") << command << endl;
36ef6d6a 679
20f90026
JS
680 char const * argv[args.size() + 1];
681 for (size_t i = 0; i < args.size(); ++i)
682 argv[i] = args[i].c_str();
683 argv[args.size()] = NULL;
684
e4e3d6b7
CM
685 char** env;
686 bool allocated;
8a398d03
DB
687 // environ can be NULL. This has been observed when running under gdb.
688 if(envVec.empty() && environ != NULL)
e4e3d6b7
CM
689 {
690 env = environ;
691 allocated = false;
692 }
693 else
694 {
695 allocated = true;
696 env = new char*[envVec.size() + 1];
697
698 for (size_t i = 0; i < envVec.size(); ++i)
699 env[i] = (char*)envVec[i].c_str();
700 env[envVec.size()] = NULL;
701 }
702
20f90026
JS
703 pid_t pid = 0;
704 int ret = posix_spawnp(&pid, argv[0], fa, NULL,
e4e3d6b7
CM
705 const_cast<char * const *>(argv), env);
706 if (allocated)
707 delete[] env;
708
01cc94dc
JS
709 PROBE2(stap, stap_system__spawn, ret, pid);
710 if (ret != 0)
36ef6d6a 711 {
db0a43c3 712 if (verbose > 1)
b530b5b3 713 clog << _F("Spawn error (%d): %s", ret, strerror(ret)) << endl;
01cc94dc 714 pid = -1;
4cc40e82 715 }
01cc94dc
JS
716 else
717 spawned_pids.insert(pid);
718 return pid;
719}
720
b63fab87
JS
721// The API version of stap_spawn doesn't expose file_actions, for now.
722pid_t
20f90026 723stap_spawn(int verbose, const vector<string>& args)
b63fab87 724{
20f90026 725 return stap_spawn(verbose, args, NULL);
b63fab87
JS
726}
727
645383d5
JS
728pid_t
729stap_spawn_piped(int verbose, const vector<string>& args,
e96f2257 730 int *child_in, int *child_out, int* child_err)
645383d5
JS
731{
732 pid_t pid = -1;
e96f2257 733 int infd[2], outfd[2], errfd[2];
645383d5
JS
734 posix_spawn_file_actions_t fa;
735 if (posix_spawn_file_actions_init(&fa) != 0)
736 return -1;
737
e96f2257 738 if (child_in && pipe_child_fd(&fa, infd, 0) != 0)
645383d5 739 goto cleanup_fa;
e96f2257
JS
740 if (child_out && pipe_child_fd(&fa, outfd, 1) != 0)
741 goto cleanup_in;
645383d5
JS
742 if (child_err && pipe_child_fd(&fa, errfd, 2) != 0)
743 goto cleanup_out;
744
745 pid = stap_spawn(verbose, args, &fa);
746
747 if (child_err)
748 {
749 if (pid > 0)
750 *child_err = errfd[0];
751 else
752 close(errfd[0]);
753 close(errfd[1]);
754 }
755
756cleanup_out:
757 if (child_out)
758 {
759 if (pid > 0)
760 *child_out = outfd[0];
761 else
762 close(outfd[0]);
763 close(outfd[1]);
764 }
765
e96f2257
JS
766cleanup_in:
767 if (child_in)
768 {
769 if (pid > 0)
770 *child_in = infd[1];
771 else
772 close(infd[1]);
773 close(infd[0]);
774 }
775
645383d5
JS
776cleanup_fa:
777 posix_spawn_file_actions_destroy(&fa);
778
779 return pid;
780}
781
e4e3d6b7
CM
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
786const set<string>&
787localization_variables()
788{
789 static set<string> localeVars;
790 if (localeVars.empty())
791 {
792 localeVars.insert("LANG");
793 localeVars.insert("LC_ALL");
794 localeVars.insert("LC_CTYPE");
795 localeVars.insert("LC_COLLATE");
796 localeVars.insert("LC_MESSAGES");
797 localeVars.insert("LC_TIME");
798 localeVars.insert("LC_MONETARY");
799 localeVars.insert("LC_NUMERIC");
800 }
801 return localeVars;
802}
803
01cc94dc
JS
804// Runs a command with a saved PID, so we can kill it from the signal handler,
805// and wait for it to finish.
806int
b13c6a37
JS
807stap_system(int verbose, const string& description,
808 const vector<string>& args,
ff520ff4 809 bool null_out, bool null_err)
01cc94dc 810{
ff520ff4
JS
811 int ret = 0;
812 posix_spawn_file_actions_t fa;
813 if (posix_spawn_file_actions_init(&fa) != 0)
814 return -1;
4cc40e82 815
ff520ff4
JS
816 if ((null_out && null_child_fd(&fa, 1) != 0) ||
817 (null_err && null_child_fd(&fa, 2) != 0))
818 ret = -1;
819 else
820 {
821 pid_t pid = stap_spawn(verbose, args, &fa);
822 ret = pid;
5def5d2a 823 if (pid > 0){
ff520ff4 824 ret = stap_waitpid(verbose, pid);
df324c9f
JS
825
826 // XXX PR13274 needs-session to use print_warning()
827 if (ret > 128)
828 clog << _F("WARNING: %s exited with signal: %d (%s)",
829 description.c_str(), ret - 128, strsignal(ret - 128)) << endl;
830 else if (ret > 0)
831 clog << _F("WARNING: %s exited with status: %d",
832 description.c_str(), ret) << endl;
5def5d2a 833 }
ff520ff4
JS
834 }
835
836 posix_spawn_file_actions_destroy(&fa);
837 return ret;
20f90026
JS
838}
839
daa75206
JS
840// Like stap_system, but capture stdout
841int
20f90026 842stap_system_read(int verbose, const vector<string>& args, ostream& out)
daa75206 843{
645383d5 844 int child_fd = -1;
e96f2257 845 pid_t child = stap_spawn_piped(verbose, args, NULL, &child_fd);
645383d5 846 if (child > 0)
daa75206 847 {
645383d5
JS
848 // read everything from the child
849 stdio_filebuf<char> in(child_fd, ios_base::in);
850 out << &in;
851 return stap_waitpid(verbose, child);
daa75206 852 }
645383d5 853 return -1;
daa75206
JS
854}
855
856
01cc94dc 857// Send a signal to our spawned commands
4cc40e82
JS
858int
859kill_stap_spawn(int sig)
860{
b30895fa 861 return spawned_pids.killall(sig);
4cc40e82
JS
862}
863
c0d1b5a0 864
8aabf152 865
c0d1b5a0
FCE
866void assert_regexp_match (const string& name, const string& value, const string& re)
867{
868 typedef map<string,regex_t*> cache;
869 static cache compiled;
870 cache::iterator it = compiled.find (re);
871 regex_t* r = 0;
872 if (it == compiled.end())
873 {
874 r = new regex_t;
875 int rc = regcomp (r, re.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED);
1d7ae21b 876 assert (rc == 0);
c0d1b5a0
FCE
877 compiled[re] = r;
878 }
879 else
880 r = it->second;
881
882 // run regexec
883 int rc = regexec (r, value.c_str(), 0, 0, 0);
884 if (rc)
e2d0f787
JS
885 throw runtime_error
886 (_F("ERROR: Safety pattern mismatch for %s ('%s' vs. '%s') rc=%d",
887 name.c_str(), value.c_str(), re.c_str(), rc));
c0d1b5a0
FCE
888}
889
890
8aabf152
FCE
891int 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 static cache compiled;
895 cache::iterator it = compiled.find (re);
896 regex_t* r = 0;
897 if (it == compiled.end())
898 {
899 r = new regex_t;
900 int rc = regcomp (r, re.c_str(), REG_EXTENDED); /* REG_ICASE? */
1d7ae21b 901 assert (rc == 0);
8aabf152
FCE
902 compiled[re] = r;
903 }
904 else
905 r = it->second;
906
907
908 // run regexec
909#define maxmatches 10
910 regmatch_t rm[maxmatches];
911
912 int rc = regexec (r, value.c_str(), maxmatches, rm, 0);
913 if (rc) return rc;
914
915 matches.erase(matches.begin(), matches.end());
916 for (unsigned i=0; i<maxmatches; i++) // XXX: ideally, the number of actual subexpressions in re
917 {
918 if (rm[i].rm_so >= 0)
919 matches.push_back(value.substr (rm[i].rm_so, rm[i].rm_eo-rm[i].rm_so));
920 else
921 matches.push_back("");
922 }
923
924 return 0;
925}
926
927
37001baa
FCE
928bool contains_glob_chars (const string& str)
929{
5750ecc6
FCE
930 for (unsigned i=0; i<str.size(); i++)
931 {
932 char this_char = str[i];
933 if (this_char == '\\' && (str.size() > i+1))
934 {
935 // PR13338: skip the escape backslash and the escaped character
936 i++;
937 continue;
938 }
939 if (this_char == '*' || this_char == '?' || this_char == '[')
940 return true;
941 }
942
943 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.
949string escape_glob_chars (const string& str)
950{
951 string op;
952 for (unsigned i=0; i<str.size(); i++)
953 {
954 char this_char = str[i];
955 if (this_char == '*' || this_char == '?' || this_char == '[')
956 op += '\\';
957 op += this_char;
958 }
959 return op;
37001baa
FCE
960}
961
5750ecc6
FCE
962string unescape_glob_chars (const string& str)
963{
964 string op;
965 for (unsigned i=0; i<str.size(); i++)
966 {
967 char this_char = str[i];
968 if (this_char == '\\' && (str.size() > i+1) )
969 {
970 op += str[i+1];
971 i++;
972 continue;
973 }
974 op += this_char;
975 }
976
977 return op;
978}
979
980
981
daa75206
JS
982string
983normalize_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.
4c25c410
FCE
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.
daa75206
JS
995
996 if (machine == "i486") return "i386";
997 else if (machine == "i586") return "i386";
998 else if (machine == "i686") return "i386";
999 else if (machine == "sun4u") return "sparc64";
1000 else if (machine.substr(0,3) == "arm") return "arm";
1001 else if (machine == "sa110") return "arm";
1002 else if (machine == "s390x") return "s390";
7ddd76f3 1003 else if (machine == "aarch64") return "arm64";
daa75206
JS
1004 else if (machine.substr(0,3) == "ppc") return "powerpc";
1005 else if (machine.substr(0,4) == "mips") return "mips";
1006 else if (machine.substr(0,3) == "sh2") return "sh";
1007 else if (machine.substr(0,3) == "sh3") return "sh";
1008 else if (machine.substr(0,3) == "sh4") return "sh";
1009 return machine;
1010}
1011
081b45d1
MW
1012int
1013elf_class_from_normalized_machine (const string &machine)
1014{
1015 // Must match kernel machine architectures as used un tapset directory.
1016 // And must match normalization done in normalize_machine ().
1017 if (machine == "i386"
1018 || machine == "arm") // arm assumes 32-bit
1019 return ELFCLASS32;
1020 else if (machine == "s390" // powerpc and s390 always assume 64-bit,
1021 || machine == "powerpc" // see normalize_machine ().
1022 || machine == "x86_64"
7ddd76f3
WC
1023 || machine == "ia64"
1024 || machine == "arm64")
081b45d1
MW
1025 return ELFCLASS64;
1026
1027 cerr << _F("Unknown kernel machine architecture '%s', don't know elf class",
1028 machine.c_str()) << endl;
1029 return -1;
1030}
1031
aeb9cc10
DB
1032string
1033kernel_release_from_build_tree (const string &kernel_build_tree, int verbose)
1034{
1035 string version_file_name = kernel_build_tree + "/include/config/kernel.release";
1036 // The file include/config/kernel.release within the
1037 // build tree is used to pull out the version information
1038 ifstream version_file (version_file_name.c_str());
1039 if (version_file.fail ())
1040 {
1041 if (verbose > 1)
1042 //TRANSLATORS: Missing a file
1043 cerr << _F("Missing %s", version_file_name.c_str()) << endl;
1044 return "";
1045 }
1046
1047 string kernel_release;
1048 char c;
1049 while (version_file.get(c) && c != '\n')
1050 kernel_release.push_back(c);
1051
1052 return kernel_release;
1053}
8aabf152 1054
cde0f3ce 1055string autosprintf(const char* format, ...)
7a1513b1
FCE
1056{
1057 va_list args;
1058 char *str;
1059 va_start (args, format);
1060 int rc = vasprintf (&str, format, args);
1061 if (rc < 0)
33631d57
CM
1062 {
1063 va_end(args);
1064 return _F("autosprintf/vasprintf error %d", rc);
1065 }
7a1513b1
FCE
1066 string s = str;
1067 va_end (args);
1068 free (str);
1069 return s; /* by copy */
1070}
1071
cde0f3ce 1072string
28946fe7
MW
1073get_self_path()
1074{
1075 char buf[1024]; // This really should be enough for anybody...
1076 const char *file = "/proc/self/exe";
1077 ssize_t len = readlink(file, buf, sizeof(buf) - 1);
1078 if (len > 0)
1079 {
1080 buf[len] = '\0';
1081 file = buf;
1082 }
1083 // otherwise the path is ridiculously large, fall back to /proc/self/exe.
1084 //
cde0f3ce 1085 return string(file);
28946fe7
MW
1086}
1087
cde0f3ce
JL
1088// String sorter using the Levenshtein algorithm
1089// TODO: Performance may be improved by adding a maximum distance
1090// parameter which would abort the operation if we know the final
1091// distance will be larger than the maximum. This may entail maintaining
1092// another data structure, and thus the cost might outweigh the benefit
1093unsigned levenshtein(const string& a, const string& b)
1094{
1095 Array2D<unsigned> d(a.size()+1, b.size()+1);
1096
1097 // border values
1098 for (unsigned i = 0; i < d.width; i++)
1099 d(i, 0) = i;
1100 for (unsigned j = 0; j < d.height; j++)
1101 d(0, j) = j;
1102
1103 // the meat
1104 for (unsigned i = 1; i < d.width; i++) {
1105 for (unsigned j = 1; j < d.height; j++) {
1106 if (a[i] == b[j]) // match
1107 d(i,j) = d(i-1, j-1);
1108 else // penalties open for adjustments
1109 d(i,j) = min(min(
1110 d(i-1,j-1) + 1, // substitution
1111 d(i-1,j) + 1), // deletion
1112 d(i,j-1) + 1); // insertion
1113 }
1114 }
1115
1116 return d(d.width-1, d.height-1);
1117}
3f95ed01
JS
1118
1119#ifndef HAVE_PPOLL
1120// This is a poor-man's ppoll, only used carefully by readers that need to be
1121// interruptible, like remote::run and mutator::run. It does not provide the
1122// same guarantee of atomicity as on systems with a true ppoll.
1123//
1124// In our use, this would cause trouble if a signal came in any time from the
1125// moment we mask signals to prepare pollfds, to the moment we call poll in
1126// emulation here. If there's no data on any of the pollfds, we will be stuck
1127// waiting indefinitely.
1128//
1129// Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
1130// throw in a one-second forced timeout to ensure we have a chance to notice
1131// there was an interrupt without too much delay.
1132int
1133ppoll(struct pollfd *fds, nfds_t nfds,
1134 const struct timespec *timeout_ts,
1135 const sigset_t *sigmask)
1136{
1137 sigset_t origmask;
1138 int timeout = (timeout_ts == NULL) ? 1000 // don't block forever...
1139 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
1140 sigprocmask(SIG_SETMASK, sigmask, &origmask);
1141 int rc = poll(fds, nfds, timeout);
1142 sigprocmask(SIG_SETMASK, &origmask, NULL);
1143 return rc;
1144}
1145#endif
1146
1147
73267b89 1148/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.270795 seconds and 5 git commands to generate.