]> sourceware.org Git - systemtap.git/blame - util.cxx
update NEWS for PR24953
[systemtap.git] / util.cxx
CommitLineData
1b78aef5 1// Copyright (C) Andrew Tridgell 2002 (original file)
73fcca6f 2// Copyright (C) 2006-2018 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>
38bf68a8 27#include <algorithm>
e6eea51b 28#include <mutex>
1f4b9e55
DS
29#include <functional>
30#include <cctype>
31#include <locale>
1b78aef5
DS
32
33extern "C" {
081b45d1 34#include <elf.h>
4cc40e82 35#include <fcntl.h>
baba4e15 36#include <grp.h>
1b78aef5 37#include <pwd.h>
4cc40e82 38#include <spawn.h>
8bf4a144 39#include <stdio.h>
1b78aef5 40#include <stdlib.h>
4cc40e82
JS
41#include <sys/stat.h>
42#include <sys/types.h>
43#include <sys/wait.h>
44#include <unistd.h>
c0d1b5a0 45#include <regex.h>
7a1513b1 46#include <stdarg.h>
1b78aef5
DS
47}
48
49using namespace std;
77455f97 50using namespace __gnu_cxx;
1b78aef5
DS
51
52
53// Return current users home directory or die.
54const char *
55get_home_directory(void)
56{
57 const char *p = getenv("HOME");
58 if (p)
59 return p;
60
61 struct passwd *pwd = getpwuid(getuid());
62 if (pwd)
63 return pwd->pw_dir;
64
35f29567
CM
65 cerr << _("Unable to determine home directory") << endl;
66 return "/";
1b78aef5
DS
67}
68
69
b12c8986
DB
70// Get the size of a file in bytes
71size_t
72get_file_size(const string &path)
73{
74 struct stat file_info;
75
76 if (stat(path.c_str(), &file_info) == 0)
77 return file_info.st_size;
78 else
79 return 0;
80}
81
82// Get the size of a file in bytes
a5751672
JS
83size_t
84get_file_size(int fd)
85{
86 struct stat file_info;
87
88 if (fstat(fd, &file_info) == 0)
89 return file_info.st_size;
90 else
91 return 0;
92}
93
94// Check that a file is present
b12c8986
DB
95bool
96file_exists (const string &path)
97{
98 struct stat file_info;
99
100 if (stat(path.c_str(), &file_info) == 0)
101 return true;
102
103 return false;
104}
105
f3fdcc93
HT
106// Check that a dir is present
107bool
108dir_exists(const string &path)
109{
110 struct stat info;
111
112 if (stat(path.c_str(), &info) == 0 &&
113 S_ISDIR(info.st_mode))
114 return true;
115
116 return false;
117}
118
1b78aef5
DS
119// Copy a file. The copy is done via a temporary file and atomic
120// rename.
e16dc041
JS
121bool
122copy_file(const string& src, const string& dest, bool verbose)
1b78aef5
DS
123{
124 int fd1, fd2;
125 char buf[10240];
126 int n;
127 string tmp;
128 char *tmp_name;
129 mode_t mask;
130
e16dc041 131 if (verbose)
b530b5b3 132 clog << _F("Copying %s to %s", src.c_str(), dest.c_str()) << endl;
e16dc041 133
1b78aef5 134 // Open the src file.
e16dc041 135 fd1 = open(src.c_str(), O_RDONLY);
1b78aef5 136 if (fd1 == -1)
e16dc041 137 goto error;
1b78aef5
DS
138
139 // Open the temporary output file.
140 tmp = dest + string(".XXXXXX");
141 tmp_name = (char *)tmp.c_str();
142 fd2 = mkstemp(tmp_name);
143 if (fd2 == -1)
144 {
145 close(fd1);
e16dc041 146 goto error;
1b78aef5
DS
147 }
148
149 // Copy the src file to the temporary output file.
150 while ((n = read(fd1, buf, sizeof(buf))) > 0)
151 {
152 if (write(fd2, buf, n) != n)
153 {
154 close(fd2);
155 close(fd1);
156 unlink(tmp_name);
e16dc041 157 goto error;
1b78aef5
DS
158 }
159 }
160 close(fd1);
161
162 // Set the permissions on the temporary output file.
163 mask = umask(0);
164 fchmod(fd2, 0666 & ~mask);
165 umask(mask);
166
167 // Close the temporary output file. The close can fail on NFS if
168 // out of space.
169 if (close(fd2) == -1)
170 {
171 unlink(tmp_name);
e16dc041 172 goto error;
1b78aef5
DS
173 }
174
175 // Rename the temporary output file to the destination file.
e16dc041 176 if (rename(tmp_name, dest.c_str()) == -1)
1b78aef5
DS
177 {
178 unlink(tmp_name);
e16dc041 179 goto error;
1b78aef5
DS
180 }
181
e16dc041
JS
182 return true;
183
184error:
b530b5b3
LB
185 cerr << _F("Copy failed (\"%s\" to \"%s\"): %s", src.c_str(),
186 dest.c_str(), strerror(errno)) << endl;
e16dc041 187 return false;
1b78aef5
DS
188}
189
190
191// Make sure a directory exists.
192int
3b6f3bbb 193create_dir(const char *dir, int mode)
1b78aef5
DS
194{
195 struct stat st;
196 if (stat(dir, &st) == 0)
197 {
198 if (S_ISDIR(st.st_mode))
199 return 0;
200 errno = ENOTDIR;
201 return 1;
202 }
203
3b6f3bbb
DB
204 // Create the directory. We must create each component
205 // of the path ourselves.
206 vector<string> components;
207 tokenize (dir, components, "/");
208 string path;
209 if (*dir == '/')
210 {
211 // Absolute path
212 path = "/";
213 }
214 unsigned limit = components.size ();
215 assert (limit != 0);
216 for (unsigned ix = 0; ix < limit; ++ix)
217 {
218 path += components[ix] + '/';
219 if (mkdir(path.c_str (), mode) != 0 && errno != EEXIST)
220 return 1;
221 }
1b78aef5
DS
222
223 return 0;
224}
225
98f552c2
DB
226// Remove a file or directory
227int
228remove_file_or_dir (const char *name)
229{
230 int rc;
231 struct stat st;
232
233 if ((rc = stat(name, &st)) != 0)
234 {
235 if (errno == ENOENT)
236 return 0;
237 return 1;
238 }
239
240 if (remove (name) != 0)
241 return 1;
aeb9cc10 242
98f552c2
DB
243 return 0;
244}
1b78aef5 245
438b5a40
SC
246
247int
248appendenv (const char *env_name, const string source)
249{
250 string dirname = source.substr(0, source.rfind("/"));
251 char *env = getenv(env_name);
252 string new_env;
253
254 if (env)
255 new_env = string (env) + ":" + dirname;
256 else
257 new_env = dirname;
258
259 return setenv(env_name, new_env.c_str(), 1);
260}
261
262
3892d516
DB
263/* Obtain the gid of the given group. */
264gid_t get_gid (const char *group_name)
265{
266 struct group *stgr;
267 /* If we couldn't find the group, return an invalid number. */
268 stgr = getgrnam(group_name);
269 if (stgr == NULL)
270 return (gid_t)-1;
271 return stgr->gr_gid;
272}
273
0da3e7a0
DB
274// Determine whether the current user is in the given group
275// by gid.
baba4e15 276bool
0da3e7a0 277in_group_id (gid_t target_gid)
baba4e15 278{
baba4e15
DB
279 // According to the getgroups() man page, getgroups() may not
280 // return the effective gid, so try to match it first. */
db0a43c3 281 if (target_gid == getegid())
baba4e15
DB
282 return true;
283
284 // Get the list of the user's groups.
f429df66 285 int ngids = getgroups(0, 0); // Returns the number to allocate.
db0a43c3
RM
286 if (ngids > 0) {
287 gid_t gidlist[ngids];
288 ngids = getgroups(ngids, gidlist);
289 for (int i = 0; i < ngids; i++) {
290 // If the user is a member of the target group, then we're done.
291 if (gidlist[i] == target_gid)
292 return true;
293 }
294 }
baba4e15 295 if (ngids < 0) {
b530b5b3 296 cerr << _("Unable to retrieve group list") << endl;
baba4e15
DB
297 return false;
298 }
299
baba4e15
DB
300 // The user is not a member of the target group
301 return false;
302}
303
85007c04
DB
304/*
305 * Returns a string describing memory resource usage.
306 * Since it seems getrusage() doesn't maintain the mem related fields,
307 * this routine parses /proc/self/statm to get the statistics.
308 */
309string
310getmemusage ()
311{
312 static long sz = sysconf(_SC_PAGESIZE);
313
d54a65f6 314 long pages;
85007c04
DB
315 ostringstream oss;
316 ifstream statm("/proc/self/statm");
317 statm >> pages;
c338e2c5 318 long kb1 = pages * sz / 1024; // total program size; vmsize
85007c04 319 statm >> pages;
c338e2c5 320 long kb2 = pages * sz / 1024; // resident set size; vmrss
85007c04 321 statm >> pages;
c338e2c5
FCE
322 long kb3 = pages * sz / 1024; // shared pages
323 statm >> pages;
324 long kb4 = pages * sz / 1024; // text
325 statm >> pages;
326 (void) kb4;
327 long kb5 = pages * sz / 1024; // library
328 statm >> pages;
329 (void) kb5;
330 long kb6 = pages * sz / 1024; // data+stack
331 statm >> pages;
332 long kb7 = pages * sz / 1024; // dirty
333 (void) kb7;
334
335 oss << _F("using %ldvirt/%ldres/%ldshr/%lddata kb, ", kb1, kb2, kb3, kb6);
85007c04
DB
336 return oss.str();
337}
338
1b78aef5
DS
339void
340tokenize(const string& str, vector<string>& tokens,
9c269440 341 const string& delimiters)
1b78aef5
DS
342{
343 // Skip delimiters at beginning.
344 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
345 // Find first "non-delimiter".
346 string::size_type pos = str.find_first_of(delimiters, lastPos);
347
348 while (pos != string::npos || lastPos != string::npos)
349 {
350 // Found a token, add it to the vector.
351 tokens.push_back(str.substr(lastPos, pos - lastPos));
352 // Skip delimiters. Note the "not_of"
353 lastPos = str.find_first_not_of(delimiters, pos);
354 // Find next "non-delimiter"
355 pos = str.find_first_of(delimiters, lastPos);
356 }
357}
358
49dbe419
DB
359// Akin to tokenize(...,...), but allow tokens before the first delimeter, after the
360// last delimiter and allow internal empty tokens
361void
362tokenize_full(const string& str, vector<string>& tokens,
9c269440 363 const string& delimiters)
49dbe419
DB
364{
365 // Check for an empty string or a string of length 1. Neither can have the requested
366 // components.
367 if (str.size() <= 1)
368 return;
369
370 // Find the first delimeter.
371 string::size_type lastPos = 0;
372 string::size_type pos = str.find_first_of(delimiters, lastPos);
373 if (pos == string::npos)
374 return; // no delimeters
375
376 /* No leading empty component allowed. */
377 if (pos == lastPos)
378 ++lastPos;
379
380 assert (lastPos < str.size());
381 do
382 {
383 pos = str.find_first_of(delimiters, lastPos);
384 if (pos == string::npos)
385 break; // Final trailing component
386 // Found a token, add it to the vector.
387 tokens.push_back(str.substr (lastPos, pos - lastPos));
388 // Skip the delimiter.
389 lastPos = pos + 1;
390 }
391 while (lastPos < str.size());
392
393 // A final non-delimited token, if it is not empty.
394 if (lastPos < str.size())
395 {
396 assert (pos == string::npos);
397 tokens.push_back(str.substr (lastPos));
398 }
399}
1b78aef5 400
91699a70
JS
401// Akin to tokenize(...,"::"), but it also has to deal with C++ template
402// madness. We do this naively by balancing '<' and '>' characters. This
403// doesn't eliminate blanks either, so a leading ::scope still works.
404void
405tokenize_cxx(const string& str, vector<string>& tokens)
406{
407 int angle_count = 0;
408 string::size_type pos = 0;
409 string::size_type colon_pos = str.find("::");
410 string::size_type angle_pos = str.find_first_of("<>");
411 while (colon_pos != string::npos &&
412 (angle_count == 0 || angle_pos != string::npos))
413 {
414 if (angle_count > 0 || angle_pos < colon_pos)
415 {
416 angle_count += str.at(angle_pos) == '<' ? 1 : -1;
417 colon_pos = str.find("::", angle_pos + 1);
418 angle_pos = str.find_first_of("<>", angle_pos + 1);
419 }
420 else
421 {
422 tokens.push_back(str.substr(pos, colon_pos - pos));
423 pos = colon_pos + 2;
424 colon_pos = str.find("::", pos);
425 angle_pos = str.find_first_of("<>", pos);
426 }
427 }
428 tokens.push_back(str.substr(pos));
429}
430
004f2b9c
JL
431// Searches for lines in buf delimited by either \n, \0. Returns a vector
432// containing tuples of the type (start of line, length of line). If data
433// remains before the end of the buffer, a last line is added. All delimiters
434// are kept.
435vector<pair<const char*,int> >
436split_lines(const char *buf, size_t n)
437{
438 vector<pair<const char*,int> > lines;
439 const char *eol, *line;
440 line = eol = buf;
441 while ((size_t)(eol-buf) < n)
442 {
443 if (*eol == '\n' || *eol == '\0')
444 {
445 lines.push_back(make_pair(line, eol-line+1));
446 line = ++eol;
447 }
448 else
449 eol++;
450 }
451
452 // add any last line
453 if (eol > line)
454 lines.push_back(make_pair(line, eol-line));
455
456 return lines;
457}
91699a70 458
2041085d
VK
459static string
460follow_link(const string& name, const string& sysroot)
461{
462 char *linkname;
463 ssize_t r;
464 string retpath;
465 struct stat st;
466
467 const char *f = name.c_str();
468
469 lstat(f, &st);
470
471 linkname = (char *) malloc(st.st_size + 1);
472
473 if (linkname)
474 {
475 r = readlink(f, linkname, st.st_size + 1);
7df9f191
DS
476 if (r != -1)
477 linkname[r] = '\0';
2041085d
VK
478 /*
479 * If we have non-empty sysroot and we got link that
480 * points to absolute path name, we need to look at
481 * this path relative to sysroot itself. access and
482 * stat will follow symbolic links correctly only in
483 * case with empty sysroot.
484 */
485 while (r != -1 && linkname && linkname[0] == '/')
486 {
487 string fname1 = sysroot + linkname;
488 const char *f1 = fname1.c_str();
489 if (access(f1, X_OK) == 0
490 && stat(f1, &st) == 0
491 && S_ISREG(st.st_mode))
492 {
493 retpath = fname1;
494 break;
495 }
496 else if (lstat(f1, &st) == 0
497 && S_ISLNK(st.st_mode))
498 {
499 free(linkname);
500 linkname = (char *) malloc(st.st_size + 1);
501 if (linkname)
502 {
503 r = readlink(f1, linkname, st.st_size + 1);
7df9f191
DS
504 if (r != -1)
505 linkname[r] = '\0';
2041085d
VK
506 }
507 }
508 else
509 {
510 break;
511 }
512 }
513 }
514 free(linkname);
515
516 return retpath;
517}
518
d0a7f5a9
FCE
519// Resolve an executable name to a canonical full path name, with the
520// same policy as execvp(). A program name not containing a slash
521// will be searched along the $PATH.
522
0a567f6d
JS
523string find_executable(const string& name)
524{
525 const map<string, string> sysenv;
526 return find_executable(name, "", sysenv);
527}
528
05fb3e0c 529string find_executable(const string& name, const string& sysroot,
0a567f6d 530 const map<string, string>& sysenv,
05fb3e0c 531 const string& env_path)
1b78aef5 532{
d9736de1 533 string retpath;
1b78aef5 534
d9736de1
FCE
535 if (name.size() == 0)
536 return name;
1b78aef5 537
d0a7f5a9
FCE
538 struct stat st;
539
540 if (name.find('/') != string::npos) // slash in the path already?
541 {
05fb3e0c 542 retpath = sysroot + name;
2041085d
VK
543
544 const char *f = retpath.c_str();
545 if (sysroot != ""
546 && lstat(f, &st) == 0
547 && S_ISLNK(st.st_mode))
548 {
549 retpath = follow_link(f, sysroot);
550 }
d0a7f5a9
FCE
551 }
552 else // Nope, search $PATH.
1b78aef5 553 {
0a567f6d 554 const char *path;
05fb3e0c 555 if (sysenv.count(env_path) != 0)
0a567f6d 556 path = sysenv.find(env_path)->second.c_str();
05fb3e0c
WF
557 else
558 path = getenv(env_path.c_str());
d9736de1 559 if (path)
1b78aef5 560 {
d9736de1
FCE
561 // Split PATH up.
562 vector<string> dirs;
563 tokenize(string(path), dirs, string(":"));
30f926f0 564
d9736de1
FCE
565 // Search the path looking for the first executable of the right name.
566 for (vector<string>::iterator i = dirs.begin(); i != dirs.end(); i++)
567 {
05fb3e0c 568 string fname = sysroot + *i + "/" + name;
d9736de1 569 const char *f = fname.c_str();
30f926f0 570
d9736de1
FCE
571 // Look for a normal executable file.
572 if (access(f, X_OK) == 0
d0a7f5a9
FCE
573 && stat(f, &st) == 0
574 && S_ISREG(st.st_mode))
d9736de1
FCE
575 {
576 retpath = fname;
577 break;
578 }
2041085d
VK
579 else if (sysroot != ""
580 && lstat(f, &st) == 0
581 && S_ISLNK(st.st_mode))
582 {
583 retpath = follow_link(f, sysroot);
584 if (retpath != "")
585 {
586 break;
587 }
588 }
d9736de1
FCE
589 }
590 }
1b78aef5
DS
591 }
592
dff50e09 593
d0a7f5a9
FCE
594 // Could not find the program on the $PATH. We'll just fall back to
595 // the unqualified name, which our caller will probably fail with.
596 if (retpath == "")
05fb3e0c 597 retpath = sysroot + name;
d0a7f5a9 598
d9736de1 599 // Canonicalize the path name.
5bca76a8
JS
600 string scf = resolve_path(retpath);
601 if (!startswith(scf, sysroot))
602 throw runtime_error(_F("find_executable(): file %s not in sysroot %s",
603 scf.c_str(), sysroot.c_str()));
604 return scf;
1b78aef5 605}
8c711d30 606
d9736de1 607
dbe9d133
JL
608bool is_fully_resolved(const string& path, const string& sysroot,
609 const map<string, string>& sysenv,
610 const string& env_path)
611{
612 return !path.empty()
613 && !contains_glob_chars(path)
614 && path.find('/') != string::npos
615 && path == find_executable(path, sysroot, sysenv, env_path);
616}
d9736de1 617
8c711d30
MH
618const string cmdstr_quoted(const string& cmd)
619{
620 // original cmd : substr1
621 // or : substr1'substr2
622 // or : substr1'substr2'substr3......
623 // after quoted :
624 // every substr(even it's empty) is quoted by ''
625 // every single-quote(') is quoted by ""
626 // examples: substr1 --> 'substr1'
627 // substr1'substr2 --> 'substr1'"'"'substr2'
628
629 string quoted_cmd;
630 string quote("'");
631 string replace("'\"'\"'");
632 string::size_type pos = 0;
633
634 quoted_cmd += quote;
dff50e09
FCE
635 for (string::size_type quote_pos = cmd.find(quote, pos);
636 quote_pos != string::npos;
8c711d30
MH
637 quote_pos = cmd.find(quote, pos)) {
638 quoted_cmd += cmd.substr(pos, quote_pos - pos);
639 quoted_cmd += replace;
640 pos = quote_pos + 1;
641 }
642 quoted_cmd += cmd.substr(pos, cmd.length() - pos);
643 quoted_cmd += quote;
644
645 return quoted_cmd;
646}
647
38bf68a8
MC
648const string
649detox_path(const string& str)
650{
651 ostringstream hash;
652 for (int i=0; i<int(str.length()); i++)
653 if (isalnum(str[i]))
654 hash << str[i];
655 else
656 hash << "_";
657 hash << "_";
658 return hash.str();
659}
a5e8d632 660
5eea6ed1
JS
661const string
662cmdstr_join(const vector<string>& cmds)
663{
664 if (cmds.empty())
b530b5b3 665 throw runtime_error(_("cmdstr_join called with an empty command!"));
5eea6ed1
JS
666
667 stringstream cmd;
668 cmd << cmdstr_quoted(cmds[0]);
669 for (size_t i = 1; i < cmds.size(); ++i)
670 cmd << " " << cmdstr_quoted(cmds[i]);
671
672 return cmd.str();
673}
674
675
8fdd1f2e
DS
676const string
677join(const vector<string>& vec, const string &delim)
678{
679 if (vec.empty())
680 throw runtime_error(_("join called with an empty vector!"));
681
682 stringstream join_str;
683 join_str << vec[0];
684 for (size_t i = 1; i < vec.size(); ++i)
685 join_str << delim << vec[i];
686
687 return join_str.str();
688}
689
690
b30895fa
JS
691// signal-safe set of pids
692class spawned_pids_t {
693 private:
694 set<pid_t> pids;
e6eea51b 695
83ae46dd 696#ifndef SINGLE_THREADED
e6eea51b 697 mutex mux_pids;
83ae46dd 698#endif
4cc40e82 699
e6eea51b
JS
700 unique_lock<mutex> lock()
701 {
702#ifndef SINGLE_THREADED
703 return unique_lock<mutex>(mux_pids);
704#else
705 return {};
706#endif
707 }
708
b30895fa 709 public:
e6eea51b 710
b30895fa
JS
711 bool contains (pid_t p)
712 {
713 stap_sigmasker masked;
e6eea51b 714 auto guard = lock();
26a39006 715
26a39006 716 bool ret = (pids.count(p)==0) ? true : false;
26a39006
CM
717
718 return ret;
b30895fa 719 }
e6eea51b 720
b30895fa
JS
721 bool insert (pid_t p)
722 {
723 stap_sigmasker masked;
e6eea51b 724 auto guard = lock();
26a39006 725
26a39006 726 bool ret = (p > 0) ? pids.insert(p).second : false;
26a39006
CM
727
728 return ret;
b30895fa 729 }
e6eea51b 730
b30895fa
JS
731 void erase (pid_t p)
732 {
733 stap_sigmasker masked;
e6eea51b 734 auto guard = lock();
26a39006 735
b30895fa
JS
736 pids.erase(p);
737 }
e6eea51b 738
b30895fa
JS
739 int killall (int sig)
740 {
741 int ret = 0;
742 stap_sigmasker masked;
e6eea51b 743 auto guard = lock();
26a39006 744
a5fa9c25
JS
745 for (set<pid_t>::const_iterator it = pids.begin();
746 it != pids.end(); ++it)
b30895fa
JS
747 ret = kill(*it, sig) ?: ret;
748 return ret;
749 }
26a39006 750
b30895fa
JS
751};
752static spawned_pids_t spawned_pids;
4cc40e82 753
13efed2a
JL
754/* Returns exit code of pid if terminated nicely, or 128+signal if terminated
755 * not nicely, or -1 if waitpid() failed. So if ret >= 0, it's the rc/signal */
01cc94dc
JS
756int
757stap_waitpid(int verbose, pid_t pid)
daa75206
JS
758{
759 int ret, status;
b30895fa
JS
760 if (verbose > 1 && spawned_pids.contains(pid))
761 clog << _F("Spawn waitpid call on unmanaged pid %d", pid) << endl;
01cc94dc
JS
762 ret = waitpid(pid, &status, 0);
763 if (ret == pid)
daa75206 764 {
01cc94dc 765 spawned_pids.erase(pid);
daa75206 766 ret = WIFEXITED(status) ? WEXITSTATUS(status) : 128 + WTERMSIG(status);
483cf56e 767 if (verbose > 1)
f245e619 768 clog << _F("Spawn waitpid result (0x%x): %d", (unsigned)status, ret) << endl;
daa75206
JS
769 }
770 else
771 {
772 if (verbose > 1)
b530b5b3 773 clog << _F("Spawn waitpid error (%d): %s", ret, strerror(errno)) << endl;
daa75206
JS
774 ret = -1;
775 }
20f90026 776 PROBE2(stap, stap_system__complete, ret, pid);
daa75206
JS
777 return ret;
778}
779
b63fab87
JS
780static int
781pipe_child_fd(posix_spawn_file_actions_t* fa, int pipefd[2], int childfd)
782{
783 if (pipe(pipefd))
784 return -1;
785
786 int dir = childfd ? 1 : 0;
787 if (!fcntl(pipefd[0], F_SETFD, FD_CLOEXEC) &&
788 !fcntl(pipefd[1], F_SETFD, FD_CLOEXEC) &&
789 !posix_spawn_file_actions_adddup2(fa, pipefd[dir], childfd))
790 return 0;
791
792 close(pipefd[0]);
793 close(pipefd[1]);
794 return -1;
795}
796
ff520ff4
JS
797static int
798null_child_fd(posix_spawn_file_actions_t* fa, int childfd)
799{
800 int flags = childfd ? O_WRONLY : O_RDONLY;
801 return posix_spawn_file_actions_addopen(fa, childfd, "/dev/null", flags, 0);
802}
803
4cc40e82 804// Runs a command with a saved PID, so we can kill it from the signal handler
aeb9cc10 805pid_t
20f90026 806stap_spawn(int verbose, const vector<string>& args,
e4e3d6b7 807 posix_spawn_file_actions_t* fa, const vector<string>& envVec)
4cc40e82 808{
5def5d2a
LB
809 string::const_iterator it;
810 it = args[0].begin();
20f90026 811 string command;
5def5d2a 812 if(*it == '/' && (access(args[0].c_str(), X_OK)==-1)) //checking to see if staprun is executable
2713ea24
CM
813 // XXX PR13274 needs-session to use print_warning()
814 clog << _F("WARNING: %s is not executable (%s)", args[0].c_str(), strerror(errno)) << endl;
20f90026
JS
815 for (size_t i = 0; i < args.size(); ++i)
816 command += " " + args[i];
1acfc030 817 PROBE1(stap, stap_system__start, command.c_str());
db0a43c3 818 if (verbose > 1)
b530b5b3 819 clog << _("Running") << command << endl;
36ef6d6a 820
20f90026
JS
821 char const * argv[args.size() + 1];
822 for (size_t i = 0; i < args.size(); ++i)
823 argv[i] = args[i].c_str();
824 argv[args.size()] = NULL;
825
e4e3d6b7
CM
826 char** env;
827 bool allocated;
8a398d03
DB
828 // environ can be NULL. This has been observed when running under gdb.
829 if(envVec.empty() && environ != NULL)
e4e3d6b7
CM
830 {
831 env = environ;
832 allocated = false;
833 }
834 else
835 {
836 allocated = true;
837 env = new char*[envVec.size() + 1];
838
839 for (size_t i = 0; i < envVec.size(); ++i)
840 env[i] = (char*)envVec[i].c_str();
841 env[envVec.size()] = NULL;
842 }
843
20f90026
JS
844 pid_t pid = 0;
845 int ret = posix_spawnp(&pid, argv[0], fa, NULL,
e4e3d6b7
CM
846 const_cast<char * const *>(argv), env);
847 if (allocated)
848 delete[] env;
849
01cc94dc
JS
850 PROBE2(stap, stap_system__spawn, ret, pid);
851 if (ret != 0)
36ef6d6a 852 {
db0a43c3 853 if (verbose > 1)
b530b5b3 854 clog << _F("Spawn error (%d): %s", ret, strerror(ret)) << endl;
01cc94dc 855 pid = -1;
4cc40e82 856 }
01cc94dc
JS
857 else
858 spawned_pids.insert(pid);
859 return pid;
860}
861
b63fab87
JS
862// The API version of stap_spawn doesn't expose file_actions, for now.
863pid_t
20f90026 864stap_spawn(int verbose, const vector<string>& args)
b63fab87 865{
20f90026 866 return stap_spawn(verbose, args, NULL);
b63fab87
JS
867}
868
645383d5
JS
869pid_t
870stap_spawn_piped(int verbose, const vector<string>& args,
e96f2257 871 int *child_in, int *child_out, int* child_err)
645383d5
JS
872{
873 pid_t pid = -1;
e96f2257 874 int infd[2], outfd[2], errfd[2];
645383d5
JS
875 posix_spawn_file_actions_t fa;
876 if (posix_spawn_file_actions_init(&fa) != 0)
877 return -1;
878
e96f2257 879 if (child_in && pipe_child_fd(&fa, infd, 0) != 0)
645383d5 880 goto cleanup_fa;
e96f2257
JS
881 if (child_out && pipe_child_fd(&fa, outfd, 1) != 0)
882 goto cleanup_in;
645383d5
JS
883 if (child_err && pipe_child_fd(&fa, errfd, 2) != 0)
884 goto cleanup_out;
885
886 pid = stap_spawn(verbose, args, &fa);
887
888 if (child_err)
889 {
890 if (pid > 0)
891 *child_err = errfd[0];
892 else
893 close(errfd[0]);
894 close(errfd[1]);
895 }
896
897cleanup_out:
898 if (child_out)
899 {
900 if (pid > 0)
901 *child_out = outfd[0];
902 else
903 close(outfd[0]);
904 close(outfd[1]);
905 }
906
e96f2257
JS
907cleanup_in:
908 if (child_in)
909 {
910 if (pid > 0)
911 *child_in = infd[1];
912 else
913 close(infd[1]);
914 close(infd[0]);
915 }
916
645383d5
JS
917cleanup_fa:
918 posix_spawn_file_actions_destroy(&fa);
919
920 return pid;
921}
922
e4e3d6b7
CM
923// Global set of supported localization variables. Make changes here to
924// add or remove variables. List of variables from:
925// http://publib.boulder.ibm.com/infocenter/tivihelp/v8r1/index.jsp?topic=/
926// com.ibm.netcool_OMNIbus.doc_7.3.0/omnibus/wip/install/concept/omn_con_settingyourlocale.html
927const set<string>&
928localization_variables()
929{
930 static set<string> localeVars;
931 if (localeVars.empty())
932 {
933 localeVars.insert("LANG");
934 localeVars.insert("LC_ALL");
935 localeVars.insert("LC_CTYPE");
936 localeVars.insert("LC_COLLATE");
937 localeVars.insert("LC_MESSAGES");
938 localeVars.insert("LC_TIME");
939 localeVars.insert("LC_MONETARY");
940 localeVars.insert("LC_NUMERIC");
941 }
942 return localeVars;
943}
944
01cc94dc
JS
945// Runs a command with a saved PID, so we can kill it from the signal handler,
946// and wait for it to finish.
947int
b13c6a37
JS
948stap_system(int verbose, const string& description,
949 const vector<string>& args,
ff520ff4 950 bool null_out, bool null_err)
01cc94dc 951{
ff520ff4
JS
952 int ret = 0;
953 posix_spawn_file_actions_t fa;
954 if (posix_spawn_file_actions_init(&fa) != 0)
955 return -1;
4cc40e82 956
ff520ff4
JS
957 if ((null_out && null_child_fd(&fa, 1) != 0) ||
958 (null_err && null_child_fd(&fa, 2) != 0))
959 ret = -1;
960 else
961 {
962 pid_t pid = stap_spawn(verbose, args, &fa);
963 ret = pid;
5def5d2a 964 if (pid > 0){
ff520ff4 965 ret = stap_waitpid(verbose, pid);
df324c9f
JS
966
967 // XXX PR13274 needs-session to use print_warning()
968 if (ret > 128)
969 clog << _F("WARNING: %s exited with signal: %d (%s)",
970 description.c_str(), ret - 128, strsignal(ret - 128)) << endl;
971 else if (ret > 0)
972 clog << _F("WARNING: %s exited with status: %d",
973 description.c_str(), ret) << endl;
5def5d2a 974 }
ff520ff4
JS
975 }
976
977 posix_spawn_file_actions_destroy(&fa);
978 return ret;
20f90026
JS
979}
980
daa75206
JS
981// Like stap_system, but capture stdout
982int
20f90026 983stap_system_read(int verbose, const vector<string>& args, ostream& out)
daa75206 984{
645383d5 985 int child_fd = -1;
e96f2257 986 pid_t child = stap_spawn_piped(verbose, args, NULL, &child_fd);
645383d5 987 if (child > 0)
daa75206 988 {
645383d5
JS
989 // read everything from the child
990 stdio_filebuf<char> in(child_fd, ios_base::in);
991 out << &in;
992 return stap_waitpid(verbose, child);
daa75206 993 }
645383d5 994 return -1;
daa75206
JS
995}
996
997
9a9be966
DS
998std::pair<bool,int>
999stap_fork_read(int verbose, ostream& out)
1000{
1001 int pipefd[2];
1002 if (pipe(pipefd) != 0)
1003 return make_pair(false, -1);
1004
1005 fflush(stdout); cout.flush();
1006
1007 if (verbose > 1)
1008 clog << _("Forking subprocess...") << endl;
1009
1010 pid_t child = fork();
1011 PROBE1(stap, stap_system__fork, child);
1012 // child < 0: fork failure
1013 if (child < 0)
1014 {
1015 if (verbose > 1)
1016 clog << _F("Fork error (%d): %s", child, strerror(errno)) << endl;
1017 close(pipefd[0]);
1018 close(pipefd[1]);
1019 return make_pair(false, -1);
1020 }
1021 // child == 0: we're the child
1022 else if (child == 0)
1023 {
1024 close(pipefd[0]);
1025 fcntl(pipefd[1], F_SETFD, FD_CLOEXEC);
1026 return make_pair(true, pipefd[1]);
1027 }
1028
1029 // child > 0: we're the parent
1030 spawned_pids.insert(child);
1031
1032 // read everything from the child
1033 close(pipefd[1]);
1034 stdio_filebuf<char> in(pipefd[0], ios_base::in);
1035 out << &in;
1036 return make_pair(false, stap_waitpid(verbose, child));
1037}
1038
1039
01cc94dc 1040// Send a signal to our spawned commands
4cc40e82
JS
1041int
1042kill_stap_spawn(int sig)
1043{
b30895fa 1044 return spawned_pids.killall(sig);
4cc40e82
JS
1045}
1046
c0d1b5a0 1047
8aabf152 1048
c0d1b5a0
FCE
1049void assert_regexp_match (const string& name, const string& value, const string& re)
1050{
1051 typedef map<string,regex_t*> cache;
1052 static cache compiled;
1053 cache::iterator it = compiled.find (re);
1054 regex_t* r = 0;
1055 if (it == compiled.end())
1056 {
1057 r = new regex_t;
1058 int rc = regcomp (r, re.c_str(), REG_ICASE|REG_NOSUB|REG_EXTENDED);
1d7ae21b 1059 assert (rc == 0);
c0d1b5a0
FCE
1060 compiled[re] = r;
1061 }
1062 else
1063 r = it->second;
1064
1065 // run regexec
1066 int rc = regexec (r, value.c_str(), 0, 0, 0);
1067 if (rc)
e2d0f787
JS
1068 throw runtime_error
1069 (_F("ERROR: Safety pattern mismatch for %s ('%s' vs. '%s') rc=%d",
1070 name.c_str(), value.c_str(), re.c_str(), rc));
c0d1b5a0
FCE
1071}
1072
1073
8aabf152
FCE
1074int regexp_match (const string& value, const string& re, vector<string>& matches)
1075{
1076 typedef map<string,regex_t*> cache; // separate cache because we use different regcomp options
1077 static cache compiled;
1078 cache::iterator it = compiled.find (re);
1079 regex_t* r = 0;
1080 if (it == compiled.end())
1081 {
1082 r = new regex_t;
1083 int rc = regcomp (r, re.c_str(), REG_EXTENDED); /* REG_ICASE? */
1d7ae21b 1084 assert (rc == 0);
8aabf152
FCE
1085 compiled[re] = r;
1086 }
1087 else
1088 r = it->second;
1089
1090
1091 // run regexec
1092#define maxmatches 10
1093 regmatch_t rm[maxmatches];
1094
1095 int rc = regexec (r, value.c_str(), maxmatches, rm, 0);
1096 if (rc) return rc;
1097
1098 matches.erase(matches.begin(), matches.end());
1099 for (unsigned i=0; i<maxmatches; i++) // XXX: ideally, the number of actual subexpressions in re
1100 {
1101 if (rm[i].rm_so >= 0)
1102 matches.push_back(value.substr (rm[i].rm_so, rm[i].rm_eo-rm[i].rm_so));
1103 else
1104 matches.push_back("");
1105 }
1106
1107 return 0;
1108}
1109
1110
37001baa
FCE
1111bool contains_glob_chars (const string& str)
1112{
5750ecc6
FCE
1113 for (unsigned i=0; i<str.size(); i++)
1114 {
1115 char this_char = str[i];
1116 if (this_char == '\\' && (str.size() > i+1))
1117 {
1118 // PR13338: skip the escape backslash and the escaped character
1119 i++;
1120 continue;
1121 }
1122 if (this_char == '*' || this_char == '?' || this_char == '[')
1123 return true;
1124 }
1125
1126 return false;
1127}
1128
1129
1130// PR13338: we need these functions to be able to pass through glob metacharacters
1131// through the recursive process("...*...") expansion process.
1132string escape_glob_chars (const string& str)
1133{
1134 string op;
1135 for (unsigned i=0; i<str.size(); i++)
1136 {
1137 char this_char = str[i];
1138 if (this_char == '*' || this_char == '?' || this_char == '[')
1139 op += '\\';
1140 op += this_char;
1141 }
1142 return op;
37001baa
FCE
1143}
1144
5750ecc6
FCE
1145string unescape_glob_chars (const string& str)
1146{
1147 string op;
1148 for (unsigned i=0; i<str.size(); i++)
1149 {
1150 char this_char = str[i];
1151 if (this_char == '\\' && (str.size() > i+1) )
1152 {
1153 op += str[i+1];
1154 i++;
1155 continue;
1156 }
1157 op += this_char;
1158 }
1159
1160 return op;
1161}
1162
a81fb5d4
JU
1163// PR23391, this is still incomplete but enough is
1164// complete to handle "__{ia32,x64}_sys_$syscall"
1165// functions.
1166string csh_to_ksh (const string& csh)
1167{
1168 string ksh;
1169 for (unsigned i=0; i<csh.size(); i++)
1170 {
1171 if (csh[i] == '{')
1172 ksh += "@(";
1173 else if (csh[i] == '}')
1174 ksh += ')';
1175 else if (csh[i] == ',')
1176 ksh += '|';
1177 else
1178 ksh += csh[i];
1179 }
1180 return ksh;
1181}
1182
e207d98f
MW
1183bool identifier_string_needs_escape (const string& str)
1184{
1185 for (unsigned i = 0; i < str.size (); i++)
1186 {
1187 char this_char = str[i];
1188 if (! isalnum (this_char) && this_char != '_')
1189 return true;
1190 }
1191
1192 return false;
1193}
1194
dd8cb30b 1195string escaped_identifier_string (const string &str)
e207d98f
MW
1196{
1197 if (! identifier_string_needs_escape (str))
1198 return str;
5750ecc6 1199
e207d98f
MW
1200 string op;
1201 for (unsigned i = 0; i < str.size (); i++)
1202 {
1203 char this_char = str[i];
1204 if (! isalnum (this_char) && this_char != '_')
1205 {
1206 char b[32];
1207 sprintf (b, "_%x_", (unsigned int) this_char);
1208 op += b;
1209 }
1210 else
1211 op += this_char;
1212 }
1213
1214 return op;
1215}
5750ecc6 1216
f44430b4
SM
1217unsigned char
1218octal_character (unsigned c)
1219{
1220 return '0' + c % 8;
1221}
1222
1223string
1224escaped_character (unsigned c)
1225{
1226 ostringstream o;
1227 int oc = (int)c;
1228
1229 switch (oc)
1230 {
1231 case '\'':
1232 o << "\\'";
1233 break;
1234
1235 case '"':
1236 o << "\\\"";
1237 break;
1238
1239 case '\n':
1240 o << "\\n";
1241 break;
1242
1243 case '\t':
1244 o << "\\t";
1245 break;
1246
1247 case '\v':
1248 o << "\\v";
1249 break;
1250
1251 case '\b':
1252 o << "\\b";
1253 break;
1254
1255 case '\r':
1256 o << "\\r";
1257 break;
1258
1259 case '\f':
1260 o << "\\f";
1261 break;
1262
1263 case '\a':
1264 o << "\\a";
1265 break;
1266
1267 case '\\':
1268 o << "\\\\";
1269 break;
1270
1271 default:
1272
1273 if ((oc < 256) && isprint(oc))
1274 {
1275 o << (unsigned char) oc;
1276 }
1277 else
1278 {
1279 o << '\\' << octal_character(oc / 64)
1280 << octal_character(oc / 8)
1281 << octal_character(oc);
1282 }
1283 }
1284 return o.str();
1285}
1286
1287string
1288escaped_literal_string (const string& str)
1289{
1290 string op;
1291 for (unsigned i = 0; i < str.size (); i++)
1292 {
1293 op += escaped_character((unsigned)str[i]);
1294 }
1295 return op;
1296}
1297
daa75206
JS
1298string
1299normalize_machine(const string& machine)
1300{
1301 // PR4186: Copy logic from coreutils uname (uname -i) to squash
1302 // i?86->i386. Actually, copy logic from linux top-level Makefile
1303 // to squash uname -m -> $(SUBARCH).
1304 //
1305 // This logic needs to match the logic in the stap_get_arch shell
1306 // function in stap-env.
4c25c410
FCE
1307 //
1308 // But: RHBZ669082 reminds us that this renaming post-dates some
1309 // of the kernel versions we know and love. So in buildrun.cxx
1310 // we undo this renaming for ancient powerpc.
7dde4b6e
FCE
1311 //
1312 // NB repeated: see also stap-env (stap_get_arch)
daa75206
JS
1313 if (machine == "i486") return "i386";
1314 else if (machine == "i586") return "i386";
1315 else if (machine == "i686") return "i386";
1316 else if (machine == "sun4u") return "sparc64";
1317 else if (machine.substr(0,3) == "arm") return "arm";
1318 else if (machine == "sa110") return "arm";
1319 else if (machine == "s390x") return "s390";
7ddd76f3 1320 else if (machine == "aarch64") return "arm64";
daa75206
JS
1321 else if (machine.substr(0,3) == "ppc") return "powerpc";
1322 else if (machine.substr(0,4) == "mips") return "mips";
1323 else if (machine.substr(0,3) == "sh2") return "sh";
1324 else if (machine.substr(0,3) == "sh3") return "sh";
1325 else if (machine.substr(0,3) == "sh4") return "sh";
7dde4b6e 1326 // NB repeated: see also stap-env (stap_get_arch)
daa75206
JS
1327 return machine;
1328}
1329
081b45d1
MW
1330int
1331elf_class_from_normalized_machine (const string &machine)
1332{
1333 // Must match kernel machine architectures as used un tapset directory.
1334 // And must match normalization done in normalize_machine ().
1335 if (machine == "i386"
1336 || machine == "arm") // arm assumes 32-bit
1337 return ELFCLASS32;
1338 else if (machine == "s390" // powerpc and s390 always assume 64-bit,
1339 || machine == "powerpc" // see normalize_machine ().
1340 || machine == "x86_64"
7ddd76f3
WC
1341 || machine == "ia64"
1342 || machine == "arm64")
081b45d1
MW
1343 return ELFCLASS64;
1344
1345 cerr << _F("Unknown kernel machine architecture '%s', don't know elf class",
1346 machine.c_str()) << endl;
1347 return -1;
1348}
1349
aeb9cc10
DB
1350string
1351kernel_release_from_build_tree (const string &kernel_build_tree, int verbose)
1352{
1353 string version_file_name = kernel_build_tree + "/include/config/kernel.release";
1354 // The file include/config/kernel.release within the
1355 // build tree is used to pull out the version information
1356 ifstream version_file (version_file_name.c_str());
1357 if (version_file.fail ())
1358 {
1359 if (verbose > 1)
1360 //TRANSLATORS: Missing a file
1361 cerr << _F("Missing %s", version_file_name.c_str()) << endl;
1362 return "";
1363 }
1364
1365 string kernel_release;
1366 char c;
1367 while (version_file.get(c) && c != '\n')
1368 kernel_release.push_back(c);
1369
1370 return kernel_release;
1371}
8aabf152 1372
cde0f3ce 1373string autosprintf(const char* format, ...)
7a1513b1
FCE
1374{
1375 va_list args;
1376 char *str;
1377 va_start (args, format);
1378 int rc = vasprintf (&str, format, args);
1379 if (rc < 0)
33631d57
CM
1380 {
1381 va_end(args);
1382 return _F("autosprintf/vasprintf error %d", rc);
1383 }
7a1513b1
FCE
1384 string s = str;
1385 va_end (args);
1386 free (str);
1387 return s; /* by copy */
1388}
1389
cde0f3ce 1390string
28946fe7
MW
1391get_self_path()
1392{
1393 char buf[1024]; // This really should be enough for anybody...
1394 const char *file = "/proc/self/exe";
1395 ssize_t len = readlink(file, buf, sizeof(buf) - 1);
1396 if (len > 0)
1397 {
1398 buf[len] = '\0';
1399 file = buf;
1400 }
1401 // otherwise the path is ridiculously large, fall back to /proc/self/exe.
1402 //
cde0f3ce 1403 return string(file);
28946fe7
MW
1404}
1405
30b865ce 1406bool
a03a3744 1407is_valid_pid (pid_t pid, string& err_msg)
30b865ce
AJ
1408{
1409 err_msg = "";
b6eb07fd 1410 if (pid <= 0)
30b865ce 1411 {
b6eb07fd
AJ
1412 err_msg = _F("cannot probe pid %d: Invalid pid", pid);
1413 return false;
1414 }
1415 else if (kill(pid, 0) == -1)
1416 {
1417 err_msg = _F("cannot probe pid %d: %s", pid, strerror(errno));
30b865ce
AJ
1418 return false;
1419 }
1420 return true;
1421}
1422
cde0f3ce
JL
1423// String sorter using the Levenshtein algorithm
1424// TODO: Performance may be improved by adding a maximum distance
1425// parameter which would abort the operation if we know the final
1426// distance will be larger than the maximum. This may entail maintaining
1427// another data structure, and thus the cost might outweigh the benefit
593f09eb
JL
1428unsigned
1429levenshtein(const string& a, const string& b)
cde0f3ce
JL
1430{
1431 Array2D<unsigned> d(a.size()+1, b.size()+1);
1432
1433 // border values
1434 for (unsigned i = 0; i < d.width; i++)
1435 d(i, 0) = i;
1436 for (unsigned j = 0; j < d.height; j++)
1437 d(0, j) = j;
1438
1439 // the meat
1440 for (unsigned i = 1; i < d.width; i++) {
1441 for (unsigned j = 1; j < d.height; j++) {
a88f4aec 1442 if (a[i-1] == b[j-1]) // match
cde0f3ce
JL
1443 d(i,j) = d(i-1, j-1);
1444 else // penalties open for adjustments
75d96dc7
JL
1445 {
1446 unsigned subpen = 2; // substitution penalty
1447 // check if they are upper/lowercase related
1448 if (tolower(a[i-1]) == tolower(b[j-1]))
1449 subpen = 1; // half penalty
1450 d(i,j) = min(min(
1451 d(i-1,j-1) + subpen, // substitution
1452 d(i-1,j) + 2), // deletion
1453 d(i,j-1) + 2); // insertion
1454 }
cde0f3ce
JL
1455 }
1456 }
1457
1458 return d(d.width-1, d.height-1);
1459}
3f95ed01 1460
593f09eb
JL
1461// Returns comma-separated list of set elements closest to the target string.
1462// Print a maximum amount of 'max' elements, with a maximum levenshtein score
1463// of 'threshold'.
1464string
1465levenshtein_suggest(const string& target, // string to match against
1466 const set<string>& elems, // elements to suggest from
1467 unsigned max, // max elements to print
1468 unsigned threshold) // max leven score to print
1469{
1470 // calculate leven score for each elem and put in map
1471 multimap<unsigned, string> scores;
1472 for (set<string>::const_iterator it = elems.begin();
1473 it != elems.end(); ++it)
1474 {
1062ce54
JL
1475 if (it->empty()) // skip empty strings
1476 continue;
1477
59b11ead
JL
1478 // Approximate levenshtein by size-difference only; real score
1479 // is at least this high
1480 unsigned min_score = labs(target.size() - it->size());
1481
1482 if (min_score > threshold) // min-score too high for threshold
1483 continue;
1484
1485 /* Check if we can skip calculating the score for this element. This works
1486 * on knowing two facts:
1487 * (1) We will only print the 'max' number of the top elements. The
1488 * current top 'max' candidates reside in the scores map already.
1489 * (2) The score will be AT LEAST the difference between the lengths of
1490 * the two strings.
1491 * So what we do is retrieve the WORST score of the current best
1492 * candidates by iterating through the map (which is ordered) and
1493 * retrieving the 'max-th' item and check if that's still better than the
1494 * BEST score we could possibly get from the two strings (by comparing
1495 * their lengths). If the 'max-th' item is indeed better, then we know
1496 * this element will NEVER make it to the terminal. So we just skip it and
1497 * move on. Quite tragic if you ask me...
1498 */
1499 unsigned maxth_score = std::numeric_limits<unsigned>::max();
1500 if (scores.size() >= max) // do we have at least 'max' items?
1501 {
1502 // retrieve 'max-th' item
1503 multimap<unsigned, string>::iterator itt = scores.begin();
1504 for (unsigned i = 0; i < max-1; i++) itt++; // will not go to .end()
1505 maxth_score = itt->first;
1506 }
1507
1508 if (min_score > maxth_score) // min-score too high for known candidates
1509 continue;
1510
593f09eb 1511 unsigned score = levenshtein(target, *it);
59b11ead
JL
1512
1513 if (score > maxth_score) // actual score too high for known candidates
1514 continue;
1515
1516 if (score > threshold) // actual score too high for threshold
1517 continue;
1518
1519 // a candidate!
1520 scores.insert(make_pair(score, *it));
593f09eb
JL
1521 }
1522
1523 string suggestions;
1524
1525 // Print out the top 'max' elements
1526 multimap<unsigned, string>::iterator it = scores.begin();
1527 for (unsigned i = 0; it != scores.end() && i < max; ++it, i++)
1528 suggestions += it->second + ", ";
1529 if (!suggestions.empty())
1530 suggestions.erase(suggestions.size()-2);
1531
1532 return suggestions;
1533}
1534
a3e980f9
FCE
1535string
1536levenshtein_suggest(const string& target, // string to match against
47d349b1 1537 const set<interned_string>& elems,// elements to suggest from
a3e980f9
FCE
1538 unsigned max, // max elements to print
1539 unsigned threshold) // max leven score to print
1540{
1541 set<string> elems2;
47d349b1 1542 for (set<interned_string>::const_iterator it = elems.begin();
a3e980f9
FCE
1543 it != elems.end();
1544 it++)
1545 elems2.insert(it->to_string());
1546
1547 return levenshtein_suggest (target, elems2, max, threshold);
1548
1549}
1550
1551
3f95ed01
JS
1552#ifndef HAVE_PPOLL
1553// This is a poor-man's ppoll, only used carefully by readers that need to be
1554// interruptible, like remote::run and mutator::run. It does not provide the
1555// same guarantee of atomicity as on systems with a true ppoll.
1556//
1557// In our use, this would cause trouble if a signal came in any time from the
1558// moment we mask signals to prepare pollfds, to the moment we call poll in
1559// emulation here. If there's no data on any of the pollfds, we will be stuck
1560// waiting indefinitely.
1561//
1562// Since this is mainly about responsiveness of CTRL-C cleanup, we'll just
1563// throw in a one-second forced timeout to ensure we have a chance to notice
1564// there was an interrupt without too much delay.
1565int
1566ppoll(struct pollfd *fds, nfds_t nfds,
1567 const struct timespec *timeout_ts,
1568 const sigset_t *sigmask)
1569{
1570 sigset_t origmask;
1571 int timeout = (timeout_ts == NULL) ? 1000 // don't block forever...
1572 : (timeout_ts->tv_sec * 1000 + timeout_ts->tv_nsec / 1000000);
1573 sigprocmask(SIG_SETMASK, sigmask, &origmask);
1574 int rc = poll(fds, nfds, timeout);
1575 sigprocmask(SIG_SETMASK, &origmask, NULL);
1576 return rc;
1577}
1578#endif
1579
1580
01fb72a0
DS
1581int
1582read_from_file (const string &fname, int &data)
1583{
1584 // C++ streams may not set errno in the even of a failure. However if we
1585 // set it to 0 before each operation and it gets set during the operation,
1586 // then we can use its value in order to determine what happened.
1587 errno = 0;
1588 ifstream f (fname.c_str ());
1589 if (! f.good ())
1590 {
1591 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
1592 goto error;
1593 }
1594
1595 // Read the data;
1596 errno = 0;
1597 f >> data;
1598 if (f.fail ())
1599 {
1600 clog << _F("Unable to read from file '%s': ", fname.c_str());
1601 goto error;
1602 }
1603
1604 // NB: not necessary to f.close ();
1605 return 0; // Success
1606
1607 error:
1608 if (errno)
1609 clog << strerror (errno) << endl;
1610 else
1611 clog << _("unknown error") << endl;
1612 return 1; // Failure
1613}
1614
1615template <class T>
1616int
1617write_to_file (const string &fname, const T &data)
1618{
1619 // C++ streams may not set errno in the even of a failure. However if we
1620 // set it to 0 before each operation and it gets set during the operation,
1621 // then we can use its value in order to determine what happened.
1622 errno = 0;
1623 ofstream f (fname.c_str ());
1624 if (! f.good ())
1625 {
1626 clog << _F("Unable to open file '%s' for writing: ", fname.c_str());
1627 goto error;
1628 }
1629
1630 // Write the data;
1631 f << data;
1632 errno = 0;
1633 if (f.fail ())
1634 {
1635 clog << _F("Unable to write to file '%s': ", fname.c_str());
1636 goto error;
1637 }
1638
1639 // NB: not necessary to f.close ();
1640 return 0; // Success
1641
1642 error:
1643 if (errno)
1644 clog << strerror (errno) << endl;
1645 else
1646 clog << _("unknown error") << endl;
1647 return 1; // Failure
1648}
1649
1650// Let's go ahead an instantiate a few variants of the write_to_file()
1651// templated function.
1652template int write_to_file (const string &fname, const string &data);
586c8666 1653template int write_to_file (const string &fname, const int &data);
01fb72a0
DS
1654
1655int
1656flush_to_stream (const string &fname, ostream &o)
1657{
1658 // C++ streams may not set errno in the even of a failure. However if we
1659 // set it to 0 before each operation and it gets set during the operation,
1660 // then we can use its value in order to determine what happened.
1661 errno = 0;
1662 ifstream f (fname.c_str ());
1663 if (! f.good ())
1664 {
1665 clog << _F("Unable to open file '%s' for reading: ", fname.c_str());
1666 goto error;
1667 }
1668
1669 // Stream the data
1670
1671 // NB: o << f.rdbuf() misbehaves for some reason, appearing to close o,
1672 // which is unfortunate if o == clog or cout.
1673 while (1)
1674 {
1675 errno = 0;
1676 int c = f.get();
1677 if (f.eof ()) return 0; // normal exit
1678 if (! f.good()) break;
1679 o.put(c);
1680 if (! o.good()) break;
1681 }
1682
1683 // NB: not necessary to f.close ();
1684
1685 error:
1686 if (errno)
1687 clog << strerror (errno) << endl;
1688 else
1689 clog << _("unknown error") << endl;
1690 return 1; // Failure
1691}
1692
1f4b9e55
DS
1693// trim from start (in place)
1694void
1695ltrim(std::string &s)
1696{
1697 s.erase(s.begin(),
1698 std::find_if(s.begin(), s.end(),
1699 std::not1(std::ptr_fun<int, int>(std::isspace))));
1700}
1701
1702// trim from end (in place)
1703void
1704rtrim(std::string &s)
1705{
1706 s.erase(std::find_if(s.rbegin(), s.rend(),
1707 std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
1708}
1709
1710// trim from both ends (in place)
1711void
1712trim(std::string &s)
1713{
1714 ltrim(s);
1715 rtrim(s);
1716}
1717
109c21b7
DS
1718// Tries to determine the name and version of the running Linux OS
1719// distribution. Fills in the 'info' argument with (name, version)
1720// strings. Returns true if it was able to retrieve the information.
1721bool
1722get_distro_info(vector<string> &info)
1723{
1724 string name, version;
1725
1726 // Getting the distro name and version is harder than it should
1727 // be. We've got a multi-pronged strategy.
1728 //
1729 // (1) First, try the "lsb_release" executable, which may or may
1730 // not exist on the system.
1731 vector<string> cmd { "lsb_release", "--short", "--id" };
1732 stringstream out;
1733 int rc = stap_system_read(0, cmd, out);
1734 if (rc == 0) {
1735 name = out.str();
1736
1737 vector<string> cmd2 { "lsb_release", "--short", "--release" };
7561738b
DS
1738 stringstream out2;
1739 rc = stap_system_read(0, cmd2, out2);
109c21b7 1740 if (rc == 0) {
7561738b 1741 version = out2.str();
109c21b7
DS
1742 }
1743 }
1744
1745 // (2) Look for the /etc/os-release file.
1746 if (name.empty()) {
1747 ifstream infile;
1748 infile.open("/etc/os-release");
1749 if (infile.is_open()) {
1750 string line;
1751 while (getline(infile, line)) {
1752 vector<string> components;
1753 tokenize(line, components, "=");
75f9ecf4
DS
1754 if (components.empty())
1755 continue;
109c21b7
DS
1756 transform(components[0].begin(), components[0].end(),
1757 components[0].begin(), ::tolower);
1758 if (components[0] == "name") {
75f9ecf4
DS
1759 string::size_type pos = components[1].find(' ');
1760 if (pos == string::npos)
1761 name = components[1];
109c21b7
DS
1762 }
1763 else if (components[0] == "version_id") {
1764 version = components[1];
1765 }
75f9ecf4
DS
1766 else if (components[0] == "id" && name.empty()) {
1767 name = components[1];
1768 }
109c21b7
DS
1769 }
1770 infile.close();
1771 }
1772 }
1773
1774 // (3) Here we could look for /etc/*-release ('redhat', 'arch', 'gentoo',
1775 // etc.) or /etc/*_version ('debian', etc.), if needed.
1776
1777 info.clear();
75f9ecf4 1778 if (! name.empty()) {
1f4b9e55 1779 trim(name);
75f9ecf4 1780 // If the string is quoted, remove the quotes.
7a517c1f 1781 if (*name.begin() == '"') {
75f9ecf4
DS
1782 name.erase(0, 1);
1783 name.erase(name.size() - 1);
1784 trim(name);
1785 }
1786 }
1787 if (! version.empty()) {
1f4b9e55 1788 trim(version);
75f9ecf4 1789 // If the string is quoted, remove the quotes.
7a517c1f 1790 if (*version.begin() == '"') {
75f9ecf4
DS
1791 version.erase(0, 1);
1792 version.erase(version.size() - 1);
1793 trim(version);
1794 }
1795 }
109c21b7
DS
1796 if (! name.empty()) {
1797 info.push_back(name);
1798 info.push_back(version);
1799 return true;
1800 }
1801 return false;
1802}
1803
73267b89 1804/* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */
This page took 0.43818 seconds and 5 git commands to generate.