1 // Copyright (C) Andrew Tridgell 2002 (original file)
2 // Copyright (C) 2006-2011 Red Hat Inc. (systemtap changes)
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.
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.
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/>.
18 #include "stap-probe.h"
26 #include <ext/stdio_filebuf.h>
37 #include <sys/types.h>
42 #ifndef SINGLE_THREADED
48 using namespace __gnu_cxx
;
51 // Return current users home directory or die.
53 get_home_directory(void)
55 const char *p
= getenv("HOME");
59 struct passwd
*pwd
= getpwuid(getuid());
63 cerr
<< _("Unable to determine home directory") << endl
;
68 // Get the size of a file in bytes
70 get_file_size(const string
&path
)
72 struct stat file_info
;
74 if (stat(path
.c_str(), &file_info
) == 0)
75 return file_info
.st_size
;
80 // Get the size of a file in bytes
84 struct stat file_info
;
86 if (fstat(fd
, &file_info
) == 0)
87 return file_info
.st_size
;
92 // Check that a file is present
94 file_exists (const string
&path
)
96 struct stat file_info
;
98 if (stat(path
.c_str(), &file_info
) == 0)
104 // Copy a file. The copy is done via a temporary file and atomic
107 copy_file(const string
& src
, const string
& dest
, bool verbose
)
117 clog
<< _F("Copying %s to %s", src
.c_str(), dest
.c_str()) << endl
;
119 // Open the src file.
120 fd1
= open(src
.c_str(), O_RDONLY
);
124 // Open the temporary output file.
125 tmp
= dest
+ string(".XXXXXX");
126 tmp_name
= (char *)tmp
.c_str();
127 fd2
= mkstemp(tmp_name
);
134 // Copy the src file to the temporary output file.
135 while ((n
= read(fd1
, buf
, sizeof(buf
))) > 0)
137 if (write(fd2
, buf
, n
) != n
)
147 // Set the permissions on the temporary output file.
149 fchmod(fd2
, 0666 & ~mask
);
152 // Close the temporary output file. The close can fail on NFS if
154 if (close(fd2
) == -1)
160 // Rename the temporary output file to the destination file.
161 unlink(dest
.c_str());
162 if (rename(tmp_name
, dest
.c_str()) == -1)
171 cerr
<< _F("Copy failed (\"%s\" to \"%s\"): %s", src
.c_str(),
172 dest
.c_str(), strerror(errno
)) << endl
;
177 // Make sure a directory exists.
179 create_dir(const char *dir
, int mode
)
182 if (stat(dir
, &st
) == 0)
184 if (S_ISDIR(st
.st_mode
))
190 // Create the directory. We must create each component
191 // of the path ourselves.
192 vector
<string
> components
;
193 tokenize (dir
, components
, "/");
200 unsigned limit
= components
.size ();
202 for (unsigned ix
= 0; ix
< limit
; ++ix
)
204 path
+= components
[ix
] + '/';
205 if (mkdir(path
.c_str (), mode
) != 0 && errno
!= EEXIST
)
212 // Remove a file or directory
214 remove_file_or_dir (const char *name
)
219 if ((rc
= stat(name
, &st
)) != 0)
226 if (remove (name
) != 0)
232 /* Obtain the gid of the given group. */
233 gid_t
get_gid (const char *group_name
)
236 /* If we couldn't find the group, return an invalid number. */
237 stgr
= getgrnam(group_name
);
243 // Determine whether the current user is in the given group
246 in_group_id (gid_t target_gid
)
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())
253 // Get the list of the user's groups.
254 int ngids
= getgroups(0, 0); // Returns the number to allocate.
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
)
265 cerr
<< _("Unable to retrieve group list") << endl
;
269 // The user is not a member of the target group
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.
281 static long sz
= sysconf(_SC_PAGESIZE
);
285 ifstream
statm("/proc/self/statm");
287 long kb1
= pages
* sz
/ 1024; // total program size; vmsize
289 long kb2
= pages
* sz
/ 1024; // resident set size; vmrss
291 long kb3
= pages
* sz
/ 1024; // shared pages
293 long kb4
= pages
* sz
/ 1024; // text
296 long kb5
= pages
* sz
/ 1024; // library
299 long kb6
= pages
* sz
/ 1024; // data+stack
301 long kb7
= pages
* sz
/ 1024; // dirty
304 oss
<< _F("using %ldvirt/%ldres/%ldshr/%lddata kb, ", kb1
, kb2
, kb3
, kb6
);
309 tokenize(const string
& str
, vector
<string
>& tokens
,
310 const string
& delimiters
= " ")
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
);
317 while (pos
!= string::npos
|| lastPos
!= string::npos
)
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
);
328 // Akin to tokenize(...,...), but allow tokens before the first delimeter, after the
329 // last delimiter and allow internal empty tokens
331 tokenize_full(const string
& str
, vector
<string
>& tokens
,
332 const string
& delimiters
= " ")
334 // Check for an empty string or a string of length 1. Neither can have the requested
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
345 /* No leading empty component allowed. */
349 assert (lastPos
< str
.size());
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.
360 while (lastPos
< str
.size());
362 // A final non-delimited token, if it is not empty.
363 if (lastPos
< str
.size())
365 assert (pos
== string::npos
);
366 tokens
.push_back(str
.substr (lastPos
));
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.
374 tokenize_cxx(const string
& str
, vector
<string
>& tokens
)
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
))
383 if (angle_count
> 0 || angle_pos
< colon_pos
)
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);
391 tokens
.push_back(str
.substr(pos
, colon_pos
- pos
));
393 colon_pos
= str
.find("::", pos
);
394 angle_pos
= str
.find_first_of("<>", pos
);
397 tokens
.push_back(str
.substr(pos
));
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.
405 string
find_executable(const string
& name
)
407 const map
<string
, string
> sysenv
;
408 return find_executable(name
, "", sysenv
);
411 string
find_executable(const string
& name
, const string
& sysroot
,
412 const map
<string
, string
>& sysenv
,
413 const string
& env_path
)
417 if (name
.size() == 0)
422 if (name
.find('/') != string::npos
) // slash in the path already?
424 retpath
= sysroot
+ name
;
426 else // Nope, search $PATH.
429 if (sysenv
.count(env_path
) != 0)
430 path
= sysenv
.find(env_path
)->second
.c_str();
432 path
= getenv(env_path
.c_str());
437 tokenize(string(path
), dirs
, string(":"));
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
++)
442 string fname
= sysroot
+ *i
+ "/" + name
;
443 const char *f
= fname
.c_str();
445 // Look for a normal executable file.
446 if (access(f
, X_OK
) == 0
448 && S_ISREG(st
.st_mode
))
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.
461 retpath
= sysroot
+ name
;
463 // Canonicalize the path name.
464 char *cf
= canonicalize_file_name (retpath
.c_str());
467 string scf
= string(cf
);
471 int pos
= scf
.find(sysroot
);
475 throw runtime_error(_F("find_executable(): file %s not in sysroot %s", cf
, sysroot
.c_str()));
485 const string
cmdstr_quoted(const string
& cmd
)
487 // original cmd : substr1
488 // or : substr1'substr2
489 // or : substr1'substr2'substr3......
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'
498 string
replace("'\"'\"'");
499 string::size_type pos
= 0;
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
;
509 quoted_cmd
+= cmd
.substr(pos
, cmd
.length() - pos
);
517 cmdstr_join(const vector
<string
>& cmds
)
520 throw runtime_error(_("cmdstr_join called with an empty command!"));
523 cmd
<< cmdstr_quoted(cmds
[0]);
524 for (size_t i
= 1; i
< cmds
.size(); ++i
)
525 cmd
<< " " << cmdstr_quoted(cmds
[i
]);
531 // signal-safe set of pids
532 class spawned_pids_t
{
535 #ifndef SINGLE_THREADED
536 pthread_mutex_t mux_pids
;
540 bool contains (pid_t p
)
542 stap_sigmasker masked
;
544 #ifndef SINGLE_THREADED
545 pthread_mutex_lock(&mux_pids
);
547 bool ret
= (pids
.count(p
)==0) ? true : false;
548 #ifndef SINGLE_THREADED
549 pthread_mutex_unlock(&mux_pids
);
554 bool insert (pid_t p
)
556 stap_sigmasker masked
;
558 #ifndef SINGLE_THREADED
559 pthread_mutex_lock(&mux_pids
);
561 bool ret
= (p
> 0) ? pids
.insert(p
).second
: false;
562 #ifndef SINGLE_THREADED
563 pthread_mutex_unlock(&mux_pids
);
570 stap_sigmasker masked
;
572 #ifndef SINGLE_THREADED
573 pthread_mutex_lock(&mux_pids
);
576 #ifndef SINGLE_THREADED
577 pthread_mutex_unlock(&mux_pids
);
580 int killall (int sig
)
583 stap_sigmasker masked
;
585 #ifndef SINGLE_THREADED
586 pthread_mutex_lock(&mux_pids
);
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
);
598 #ifndef SINGLE_THREADED
599 pthread_mutex_init(&mux_pids
, NULL
);
604 #ifndef SINGLE_THREADED
605 pthread_mutex_destroy (&mux_pids
);
610 static spawned_pids_t spawned_pids
;
614 stap_waitpid(int verbose
, pid_t pid
)
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);
622 spawned_pids
.erase(pid
);
623 ret
= WIFEXITED(status
) ? WEXITSTATUS(status
) : 128 + WTERMSIG(status
);
625 clog
<< _F("Spawn waitpid result (0x%x): %d", status
, ret
) << endl
;
630 clog
<< _F("Spawn waitpid error (%d): %s", ret
, strerror(errno
)) << endl
;
633 PROBE2(stap
, stap_system__complete
, ret
, pid
);
638 pipe_child_fd(posix_spawn_file_actions_t
* fa
, int pipefd
[2], int childfd
)
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
))
655 null_child_fd(posix_spawn_file_actions_t
* fa
, int childfd
)
657 int flags
= childfd
? O_WRONLY
: O_RDONLY
;
658 return posix_spawn_file_actions_addopen(fa
, childfd
, "/dev/null", flags
, 0);
661 // Runs a command with a saved PID, so we can kill it from the signal handler
663 stap_spawn(int verbose
, const vector
<string
>& args
,
664 posix_spawn_file_actions_t
* fa
, const vector
<string
>& envVec
)
666 string::const_iterator it
;
667 it
= args
[0].begin();
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
);
678 clog
<< _("Running") << command
<< endl
;
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
;
687 // environ can be NULL. This has been observed when running under gdb.
688 if(envVec
.empty() && environ
!= NULL
)
696 env
= new char*[envVec
.size() + 1];
698 for (size_t i
= 0; i
< envVec
.size(); ++i
)
699 env
[i
] = (char*)envVec
[i
].c_str();
700 env
[envVec
.size()] = NULL
;
704 int ret
= posix_spawnp(&pid
, argv
[0], fa
, NULL
,
705 const_cast<char * const *>(argv
), env
);
709 PROBE2(stap
, stap_system__spawn
, ret
, pid
);
713 clog
<< _F("Spawn error (%d): %s", ret
, strerror(ret
)) << endl
;
717 spawned_pids
.insert(pid
);
721 // The API version of stap_spawn doesn't expose file_actions, for now.
723 stap_spawn(int verbose
, const vector
<string
>& args
)
725 return stap_spawn(verbose
, args
, NULL
);
729 stap_spawn_piped(int verbose
, const vector
<string
>& args
,
730 int *child_in
, int *child_out
, int* child_err
)
733 int infd
[2], outfd
[2], errfd
[2];
734 posix_spawn_file_actions_t fa
;
735 if (posix_spawn_file_actions_init(&fa
) != 0)
738 if (child_in
&& pipe_child_fd(&fa
, infd
, 0) != 0)
740 if (child_out
&& pipe_child_fd(&fa
, outfd
, 1) != 0)
742 if (child_err
&& pipe_child_fd(&fa
, errfd
, 2) != 0)
745 pid
= stap_spawn(verbose
, args
, &fa
);
750 *child_err
= errfd
[0];
760 *child_out
= outfd
[0];
777 posix_spawn_file_actions_destroy(&fa
);
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
787 localization_variables()
789 static set
<string
> localeVars
;
790 if (localeVars
.empty())
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");
804 // Runs a command with a saved PID, so we can kill it from the signal handler,
805 // and wait for it to finish.
807 stap_system(int verbose
, const string
& description
,
808 const vector
<string
>& args
,
809 bool null_out
, bool null_err
)
812 posix_spawn_file_actions_t fa
;
813 if (posix_spawn_file_actions_init(&fa
) != 0)
816 if ((null_out
&& null_child_fd(&fa
, 1) != 0) ||
817 (null_err
&& null_child_fd(&fa
, 2) != 0))
821 pid_t pid
= stap_spawn(verbose
, args
, &fa
);
824 ret
= stap_waitpid(verbose
, pid
);
826 // XXX PR13274 needs-session to use print_warning()
828 clog
<< _F("WARNING: %s exited with signal: %d (%s)",
829 description
.c_str(), ret
- 128, strsignal(ret
- 128)) << endl
;
831 clog
<< _F("WARNING: %s exited with status: %d",
832 description
.c_str(), ret
) << endl
;
836 posix_spawn_file_actions_destroy(&fa
);
840 // Like stap_system, but capture stdout
842 stap_system_read(int verbose
, const vector
<string
>& args
, ostream
& out
)
845 pid_t child
= stap_spawn_piped(verbose
, args
, NULL
, &child_fd
);
848 // read everything from the child
849 stdio_filebuf
<char> in(child_fd
, ios_base::in
);
851 return stap_waitpid(verbose
, child
);
857 // Send a signal to our spawned commands
859 kill_stap_spawn(int sig
)
861 return spawned_pids
.killall(sig
);
866 void assert_regexp_match (const string
& name
, const string
& value
, const string
& re
)
868 typedef map
<string
,regex_t
*> cache
;
869 static cache compiled
;
870 cache::iterator it
= compiled
.find (re
);
872 if (it
== compiled
.end())
875 int rc
= regcomp (r
, re
.c_str(), REG_ICASE
|REG_NOSUB
|REG_EXTENDED
);
883 int rc
= regexec (r
, value
.c_str(), 0, 0, 0);
886 (_F("ERROR: Safety pattern mismatch for %s ('%s' vs. '%s') rc=%d",
887 name
.c_str(), value
.c_str(), re
.c_str(), rc
));
891 int regexp_match (const string
& value
, const string
& re
, vector
<string
>& matches
)
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
);
897 if (it
== compiled
.end())
900 int rc
= regcomp (r
, re
.c_str(), REG_EXTENDED
); /* REG_ICASE? */
909 #define maxmatches 10
910 regmatch_t rm
[maxmatches
];
912 int rc
= regexec (r
, value
.c_str(), maxmatches
, rm
, 0);
915 matches
.erase(matches
.begin(), matches
.end());
916 for (unsigned i
=0; i
<maxmatches
; i
++) // XXX: ideally, the number of actual subexpressions in re
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
));
921 matches
.push_back("");
928 bool contains_glob_chars (const string
& str
)
930 for (unsigned i
=0; i
<str
.size(); i
++)
932 char this_char
= str
[i
];
933 if (this_char
== '\\' && (str
.size() > i
+1))
935 // PR13338: skip the escape backslash and the escaped character
939 if (this_char
== '*' || this_char
== '?' || this_char
== '[')
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
)
952 for (unsigned i
=0; i
<str
.size(); i
++)
954 char this_char
= str
[i
];
955 if (this_char
== '*' || this_char
== '?' || this_char
== '[')
962 string
unescape_glob_chars (const string
& str
)
965 for (unsigned i
=0; i
<str
.size(); i
++)
967 char this_char
= str
[i
];
968 if (this_char
== '\\' && (str
.size() > i
+1) )
983 normalize_machine(const string
& machine
)
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).
989 // This logic needs to match the logic in the stap_get_arch shell
990 // function in stap-env.
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.
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";
1013 elf_class_from_normalized_machine (const string
&machine
)
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
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")
1027 cerr
<< _F("Unknown kernel machine architecture '%s', don't know elf class",
1028 machine
.c_str()) << endl
;
1033 kernel_release_from_build_tree (const string
&kernel_build_tree
, int verbose
)
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 ())
1042 //TRANSLATORS: Missing a file
1043 cerr
<< _F("Missing %s", version_file_name
.c_str()) << endl
;
1047 string kernel_release
;
1049 while (version_file
.get(c
) && c
!= '\n')
1050 kernel_release
.push_back(c
);
1052 return kernel_release
;
1055 string
autosprintf(const char* format
, ...)
1059 va_start (args
, format
);
1060 int rc
= vasprintf (&str
, format
, args
);
1064 return _F("autosprintf/vasprintf error %d", rc
);
1069 return s
; /* by copy */
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);
1083 // otherwise the path is ridiculously large, fall back to /proc/self/exe.
1085 return string(file
);
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
)
1095 Array2D
<unsigned> d(a
.size()+1, b
.size()+1);
1098 for (unsigned i
= 0; i
< d
.width
; i
++)
1100 for (unsigned j
= 0; j
< d
.height
; j
++)
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
1110 d(i
-1,j
-1) + 1, // substitution
1111 d(i
-1,j
) + 1), // deletion
1112 d(i
,j
-1) + 1); // insertion
1116 return d(d
.width
-1, d
.height
-1);
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.
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.
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.
1133 ppoll(struct pollfd
*fds
, nfds_t nfds
,
1134 const struct timespec
*timeout_ts
,
1135 const sigset_t
*sigmask
)
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
);
1148 /* vim: set sw=2 ts=8 cino=>4,n-2,{2,^-2,t0,(0,u0,w1,M1 : */