]> sourceware.org Git - systemtap.git/blob - util.cxx
Add am64 support
[systemtap.git] / util.cxx
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 get_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
63 cerr << _("Unable to determine home directory") << endl;
64 return "/";
65 }
66
67
68 // Get the size of a file in bytes
69 size_t
70 get_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
81 size_t
82 get_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
93 bool
94 file_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
104 // Copy a file. The copy is done via a temporary file and atomic
105 // rename.
106 bool
107 copy_file(const string& src, const string& dest, bool verbose)
108 {
109 int fd1, fd2;
110 char buf[10240];
111 int n;
112 string tmp;
113 char *tmp_name;
114 mode_t mask;
115
116 if (verbose)
117 clog << _F("Copying %s to %s", src.c_str(), dest.c_str()) << endl;
118
119 // Open the src file.
120 fd1 = open(src.c_str(), O_RDONLY);
121 if (fd1 == -1)
122 goto error;
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);
131 goto error;
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);
142 goto error;
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);
157 goto error;
158 }
159
160 // Rename the temporary output file to the destination file.
161 unlink(dest.c_str());
162 if (rename(tmp_name, dest.c_str()) == -1)
163 {
164 unlink(tmp_name);
165 goto error;
166 }
167
168 return true;
169
170 error:
171 cerr << _F("Copy failed (\"%s\" to \"%s\"): %s", src.c_str(),
172 dest.c_str(), strerror(errno)) << endl;
173 return false;
174 }
175
176
177 // Make sure a directory exists.
178 int
179 create_dir(const char *dir, int mode)
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
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 }
208
209 return 0;
210 }
211
212 // Remove a file or directory
213 int
214 remove_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;
228
229 return 0;
230 }
231
232 /* Obtain the gid of the given group. */
233 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 stgr = getgrnam(group_name);
238 if (stgr == NULL)
239 return (gid_t)-1;
240 return stgr->gr_gid;
241 }
242
243 // Determine whether the current user is in the given group
244 // by gid.
245 bool
246 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 if (target_gid == getegid())
251 return true;
252
253 // Get the list of the user's groups.
254 int ngids = getgroups(0, 0); // Returns the number to allocate.
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 }
264 if (ngids < 0) {
265 cerr << _("Unable to retrieve group list") << endl;
266 return false;
267 }
268
269 // The user is not a member of the target group
270 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 getmemusage ()
280 {
281 static long sz = sysconf(_SC_PAGESIZE);
282
283 long pages;
284 ostringstream oss;
285 ifstream statm("/proc/self/statm");
286 statm >> pages;
287 long kb1 = pages * sz / 1024; // total program size; vmsize
288 statm >> pages;
289 long kb2 = pages * sz / 1024; // resident set size; vmrss
290 statm >> pages;
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);
305 return oss.str();
306 }
307
308 void
309 tokenize(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
328 // Akin to tokenize(...,...), but allow tokens before the first delimeter, after the
329 // last delimiter and allow internal empty tokens
330 void
331 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 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 }
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 tokenize_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
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 string find_executable(const string& name)
406 {
407 const map<string, string> sysenv;
408 return find_executable(name, "", sysenv);
409 }
410
411 string find_executable(const string& name, const string& sysroot,
412 const map<string, string>& sysenv,
413 const string& env_path)
414 {
415 string retpath;
416
417 if (name.size() == 0)
418 return name;
419
420 struct stat st;
421
422 if (name.find('/') != string::npos) // slash in the path already?
423 {
424 retpath = sysroot + name;
425 }
426 else // Nope, search $PATH.
427 {
428 const char *path;
429 if (sysenv.count(env_path) != 0)
430 path = sysenv.find(env_path)->second.c_str();
431 else
432 path = getenv(env_path.c_str());
433 if (path)
434 {
435 // Split PATH up.
436 vector<string> dirs;
437 tokenize(string(path), dirs, string(":"));
438
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 {
442 string fname = sysroot + *i + "/" + name;
443 const char *f = fname.c_str();
444
445 // Look for a normal executable file.
446 if (access(f, X_OK) == 0
447 && stat(f, &st) == 0
448 && S_ISREG(st.st_mode))
449 {
450 retpath = fname;
451 break;
452 }
453 }
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 if (retpath == "")
461 retpath = sysroot + name;
462
463 // Canonicalize the path name.
464 char *cf = canonicalize_file_name (retpath.c_str());
465 if (cf)
466 {
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 }
477 free (cf);
478 }
479
480 return retpath;
481 }
482
483
484
485 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 string quoted_cmd;
497 string quote("'");
498 string replace("'\"'\"'");
499 string::size_type pos = 0;
500
501 quoted_cmd += quote;
502 for (string::size_type quote_pos = cmd.find(quote, pos);
503 quote_pos != string::npos;
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
515
516 const string
517 cmdstr_join(const vector<string>& cmds)
518 {
519 if (cmds.empty())
520 throw runtime_error(_("cmdstr_join called with an empty command!"));
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
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 bool contains (pid_t p)
541 {
542 stap_sigmasker masked;
543
544 #ifndef SINGLE_THREADED
545 pthread_mutex_lock(&mux_pids);
546 #endif
547 bool ret = (pids.count(p)==0) ? true : false;
548 #ifndef SINGLE_THREADED
549 pthread_mutex_unlock(&mux_pids);
550 #endif
551
552 return ret;
553 }
554 bool insert (pid_t p)
555 {
556 stap_sigmasker masked;
557
558 #ifndef SINGLE_THREADED
559 pthread_mutex_lock(&mux_pids);
560 #endif
561 bool ret = (p > 0) ? pids.insert(p).second : false;
562 #ifndef SINGLE_THREADED
563 pthread_mutex_unlock(&mux_pids);
564 #endif
565
566 return ret;
567 }
568 void erase (pid_t p)
569 {
570 stap_sigmasker masked;
571
572 #ifndef SINGLE_THREADED
573 pthread_mutex_lock(&mux_pids);
574 #endif
575 pids.erase(p);
576 #ifndef SINGLE_THREADED
577 pthread_mutex_unlock(&mux_pids);
578 #endif
579 }
580 int killall (int sig)
581 {
582 int ret = 0;
583 stap_sigmasker masked;
584
585 #ifndef SINGLE_THREADED
586 pthread_mutex_lock(&mux_pids);
587 #endif
588 for (set<pid_t>::const_iterator it = pids.begin();
589 it != pids.end(); ++it)
590 ret = kill(*it, sig) ?: ret;
591 #ifndef SINGLE_THREADED
592 pthread_mutex_unlock(&mux_pids);
593 #endif
594 return ret;
595 }
596 spawned_pids_t()
597 {
598 #ifndef SINGLE_THREADED
599 pthread_mutex_init(&mux_pids, NULL);
600 #endif
601 }
602 ~spawned_pids_t()
603 {
604 #ifndef SINGLE_THREADED
605 pthread_mutex_destroy (&mux_pids);
606 #endif
607 }
608
609 };
610 static spawned_pids_t spawned_pids;
611
612
613 int
614 stap_waitpid(int verbose, pid_t pid)
615 {
616 int ret, status;
617 if (verbose > 1 && spawned_pids.contains(pid))
618 clog << _F("Spawn waitpid call on unmanaged pid %d", pid) << endl;
619 ret = waitpid(pid, &status, 0);
620 if (ret == pid)
621 {
622 spawned_pids.erase(pid);
623 ret = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
624 if (verbose > 1)
625 clog << _F("Spawn waitpid result (0x%x): %d", status, ret) << endl;
626 }
627 else
628 {
629 if (verbose > 1)
630 clog << _F("Spawn waitpid error (%d): %s", ret, strerror(errno)) << endl;
631 ret = -1;
632 }
633 PROBE2(stap, stap_system__complete, ret, pid);
634 return ret;
635 }
636
637 static int
638 pipe_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
654 static int
655 null_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
661 // Runs a command with a saved PID, so we can kill it from the signal handler
662 pid_t
663 stap_spawn(int verbose, const vector<string>& args,
664 posix_spawn_file_actions_t* fa, const vector<string>& envVec)
665 {
666 string::const_iterator it;
667 it = args[0].begin();
668 const char *cmd;
669 string command;
670 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 clog << _F("WARNING: %s is not executable (%s)", args[0].c_str(), strerror(errno)) << endl;
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);
677 if (verbose > 1)
678 clog << _("Running") << command << endl;
679
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
685 char** env;
686 bool allocated;
687 // environ can be NULL. This has been observed when running under gdb.
688 if(envVec.empty() && environ != NULL)
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
703 pid_t pid = 0;
704 int ret = posix_spawnp(&pid, argv[0], fa, NULL,
705 const_cast<char * const *>(argv), env);
706 if (allocated)
707 delete[] env;
708
709 PROBE2(stap, stap_system__spawn, ret, pid);
710 if (ret != 0)
711 {
712 if (verbose > 1)
713 clog << _F("Spawn error (%d): %s", ret, strerror(ret)) << endl;
714 pid = -1;
715 }
716 else
717 spawned_pids.insert(pid);
718 return pid;
719 }
720
721 // The API version of stap_spawn doesn't expose file_actions, for now.
722 pid_t
723 stap_spawn(int verbose, const vector<string>& args)
724 {
725 return stap_spawn(verbose, args, NULL);
726 }
727
728 pid_t
729 stap_spawn_piped(int verbose, const vector<string>& args,
730 int *child_in, int *child_out, int* child_err)
731 {
732 pid_t pid = -1;
733 int infd[2], outfd[2], errfd[2];
734 posix_spawn_file_actions_t fa;
735 if (posix_spawn_file_actions_init(&fa) != 0)
736 return -1;
737
738 if (child_in && pipe_child_fd(&fa, infd, 0) != 0)
739 goto cleanup_fa;
740 if (child_out && pipe_child_fd(&fa, outfd, 1) != 0)
741 goto cleanup_in;
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
756 cleanup_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
766 cleanup_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
776 cleanup_fa:
777 posix_spawn_file_actions_destroy(&fa);
778
779 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 localization_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
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 stap_system(int verbose, const string& description,
808 const vector<string>& args,
809 bool null_out, bool null_err)
810 {
811 int ret = 0;
812 posix_spawn_file_actions_t fa;
813 if (posix_spawn_file_actions_init(&fa) != 0)
814 return -1;
815
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;
823 if (pid > 0){
824 ret = stap_waitpid(verbose, pid);
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;
833 }
834 }
835
836 posix_spawn_file_actions_destroy(&fa);
837 return ret;
838 }
839
840 // Like stap_system, but capture stdout
841 int
842 stap_system_read(int verbose, const vector<string>& args, ostream& out)
843 {
844 int child_fd = -1;
845 pid_t child = stap_spawn_piped(verbose, args, NULL, &child_fd);
846 if (child > 0)
847 {
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);
852 }
853 return -1;
854 }
855
856
857 // Send a signal to our spawned commands
858 int
859 kill_stap_spawn(int sig)
860 {
861 return spawned_pids.killall(sig);
862 }
863
864
865
866 void 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);
876 assert (rc == 0);
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)
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));
888 }
889
890
891 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 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? */
901 assert (rc == 0);
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
928 bool contains_glob_chars (const string& str)
929 {
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.
949 string 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;
960 }
961
962 string 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
982 string
983 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 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";
1003 else if (machine == "aarch64") return "arm64";
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
1012 int
1013 elf_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"
1023 || machine == "ia64"
1024 || machine == "arm64")
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
1032 string
1033 kernel_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 }
1054
1055 string autosprintf(const char* format, ...)
1056 {
1057 va_list args;
1058 char *str;
1059 va_start (args, format);
1060 int rc = vasprintf (&str, format, args);
1061 if (rc < 0)
1062 {
1063 va_end(args);
1064 return _F("autosprintf/vasprintf error %d", rc);
1065 }
1066 string s = str;
1067 va_end (args);
1068 free (str);
1069 return s; /* by copy */
1070 }
1071
1072 string
1073 get_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 //
1085 return string(file);
1086 }
1087
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
1093 unsigned 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 }
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.
1132 int
1133 ppoll(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
1148 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.086573 seconds and 5 git commands to generate.