root/execwrappers.cpp
/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- isPerformingCkptRestart
- isBlacklistedProgram
- pthread_atfork_prepare
- pthread_atfork_parent
- pthread_atfork_child
- fork
- vfork
- execShortLivedProcessAndExit
- dmtcpPrepareForExec
- dmtcpProcessFailedExec
- getUpdatedLdPreload
- copyEnv
- stringVectorToPointerArray
- isImportantEnv
- patchUserEnv
- execve
- execv
- execvp
- execvpe
- fexecve
- execl
- execlp
- execle
- system
1 /****************************************************************************
2 * Copyright (C) 2006-2013 by Jason Ansel, Kapil Arya, and Gene Cooperman *
3 * jansel@csail.mit.edu, kapil@ccs.neu.edu, gene@ccs.neu.edu *
4 * *
5 * This file is part of DMTCP. *
6 * *
7 * DMTCP is free software: you can redistribute it and/or *
8 * modify it under the terms of the GNU Lesser General Public License as *
9 * published by the Free Software Foundation, either version 3 of the *
10 * License, or (at your option) any later version. *
11 * *
12 * DMTCP is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU Lesser General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU Lesser General Public *
18 * License along with DMTCP:dmtcp/src. If not, see *
19 * <http://www.gnu.org/licenses/>. *
20 ****************************************************************************/
21
22 #include <sys/syscall.h>
23 #ifdef __aarch64__
24 # define __ARCH_WANT_SYSCALL_DEPRECATED
25 // SYS_fork is a deprecated kernel call in aarch64; in favor of SYS_clone?
26 # include <asm-generic/unistd.h>
27 // SYS_fork undefined in aarch64, but add extra insurance
28 # undef SYS_fork
29 # define SYS_fork __NR_fork
30 #endif
31 #include "constants.h"
32 #include "uniquepid.h"
33 #include "dmtcpworker.h"
34 #include "processinfo.h"
35 #include "syscallwrappers.h"
36 #include "syslogwrappers.h"
37 #include "util.h"
38 #include "coordinatorapi.h"
39 #include "mtcpinterface.h"
40 #include "shareddata.h"
41 #include "threadsync.h"
42 #include "../jalib/jconvert.h"
43 #include "../jalib/jassert.h"
44 #include "../jalib/jfilesystem.h"
45
46 #define INITIAL_ARGV_MAX 32
47
48 using namespace dmtcp;
49
50 #ifdef DEBUG
51 const static bool dbg = true;
52 #else
53 const static bool dbg = false;
54 #endif
55
56 static bool pthread_atfork_enabled = false;
57 static uint64_t child_time;
58 static CoordinatorAPI coordinatorAPI;
59
60 // Allow plugins to call fork/exec/system to perform specific tasks during
61 // preCKpt/postCkpt/PostRestart etc. event.
62 static bool isPerformingCkptRestart()
63 {
64 if (WorkerState::currentState() != WorkerState::UNKNOWN &&
65 WorkerState::currentState() != WorkerState::RUNNING) {
66 return true;
67 }
68 return false;
69 }
70
71 static bool isBlacklistedProgram(const char *path)
72 {
73 string programName = jalib::Filesystem::BaseName(path);
74
75 JASSERT(programName != "dmtcp_coordinator" &&
76 programName != "dmtcp_launch" &&
77 programName != "dmtcp_restart" &&
78 programName != "mtcp_restart")
79 (programName) .Text("This program should not be run under ckpt control");
80
81 /*
82 * When running gdb or any shell which does a waitpid() on the child
83 * processes, executing dmtcp_command from within gdb session / shell results
84 * in process getting hung up because:
85 * gdb shell dmtcp_command -c => hangs because gdb forks off a new process
86 * and it does a waitpid (in which we block signals) ...
87 */
88 if (programName == "dmtcp_command") {
89 //make sure coordinator connection is closed
90 _real_close (PROTECTED_COORD_FD);
91
92 pid_t cpid = _real_fork();
93 JASSERT(cpid != -1);
94 if (cpid != 0) {
95 _real_exit(0);
96 }
97 }
98
99 if (programName == "dmtcp_nocheckpoint" || programName == "dmtcp_command" ||
100 programName == "ssh") {
101 return true;
102 }
103 return false;
104 }
105
106 LIB_PRIVATE void pthread_atfork_prepare()
107 {
108 /* FIXME: The user process might register a fork prepare handler with
109 * pthread_atfork. That handler will be called _after_ we have acquired the
110 * wrapper-exec lock in exclusive mode. This can lead to a deadlock situation
111 * if the user process decides to do some operations that require calling a
112 * dmtcp wrapper that require the wrapper-exec lock.
113 *
114 * Also, the preparation that dmtcp needs to do for fork() should be done
115 * right before the child process is created, i.e. after all the user
116 * handlers have been invoked. Fortunately, pthread_atfork prepare handlers
117 * are called in reverse order or registration (as opposed to parent and
118 * child handlers which are called in the order of registration), thus our
119 * prepare handle will be called at the very last.
120 *
121 * FIXME: PID-conflict detection poses yet another serious problem. On a
122 * pid-conflict, _real_fork() will be called more than once, resulting in
123 * multiple calls of user-defined prepare handlers. This is undesired and can
124 * cause several issues. One solution to this problem is to call the fork
125 * system call directly whenever a tid-conflict is detected, however, it
126 * might have some other side-effects. Another possible solution would be to
127 * have pid-virtualization plugin, which always assigns virtual pids, to the
128 * newly created processes, and thus avoiding the pid-conflict totally.
129 */
130 return;
131 }
132
133 LIB_PRIVATE void pthread_atfork_parent()
134 {
135 return;
136 }
137
138 LIB_PRIVATE void pthread_atfork_child()
139 {
140 if (!pthread_atfork_enabled) {
141 return;
142 }
143 pthread_atfork_enabled = false;
144
145 uint64_t host = UniquePid::ThisProcess().hostid();
146 UniquePid parent = UniquePid::ThisProcess();
147 UniquePid child = UniquePid(host, getpid(), child_time);
148 string child_name = jalib::Filesystem::GetProgramName() + "_(forked)";
149 _dmtcp_remutex_on_fork();
150 ThreadSync::resetLocks();
151
152 UniquePid::resetOnFork(child);
153 Util::initializeLogFile(child_name);
154
155 ProcessInfo::instance().resetOnFork();
156
157 JTRACE("fork()ed [CHILD]") (child) (parent);
158 CoordinatorAPI::resetOnFork(coordinatorAPI);
159 DmtcpWorker::resetOnFork();
160 }
161
162 extern "C" pid_t fork()
163 {
164 if (isPerformingCkptRestart()) {
165 return _real_syscall(SYS_fork);
166 }
167
168 /* Acquire the wrapperExeution lock to prevent checkpoint to happen while
169 * processing this system call.
170 */
171 WRAPPER_EXECUTION_GET_EXCL_LOCK();
172 DmtcpWorker::eventHook(DMTCP_EVENT_ATFORK_PREPARE, NULL);
173
174 /* Little bit cheating here: child_time should be same for both parent and
175 * child, thus we compute it before forking the child. */
176 child_time = time(NULL);
177 uint64_t host = UniquePid::ThisProcess().hostid();
178 UniquePid parent = UniquePid::ThisProcess();
179 string child_name = jalib::Filesystem::GetProgramName() + "_(forked)";
180
181 coordinatorAPI.createNewConnectionBeforeFork(child_name);
182
183 //Enable the pthread_atfork child call
184 pthread_atfork_enabled = true;
185 pid_t childPid = _real_fork();
186
187 if (childPid == -1) {
188 } else if (childPid == 0) { /* child process */
189 /* NOTE: Any work that needs to be done for the newly created child
190 * should be put into pthread_atfork_child() function. That function is
191 * hooked to the libc:fork() and will be called right after the new
192 * process is created and before the fork() returns.
193 *
194 * pthread_atfork_child is registered by calling pthread_atfork() from
195 * within the DmtcpWorker constructor to make sure that this is the first
196 * registered handle.
197 */
198 UniquePid child = UniquePid(host, getpid(), child_time);
199 JTRACE("fork() done [CHILD]") (child) (parent);
200
201 initializeMtcpEngine();
202 } else if (childPid > 0) { /* Parent Process */
203 UniquePid child = UniquePid(host, childPid, child_time);
204 ProcessInfo::instance().insertChild(childPid, child);
205 JTRACE("fork()ed [PARENT] done") (child);;
206 }
207
208 pthread_atfork_enabled = false;
209
210 if (childPid != 0) {
211 coordinatorAPI.closeConnection();
212 DmtcpWorker::eventHook(DMTCP_EVENT_ATFORK_PARENT, NULL);
213 WRAPPER_EXECUTION_RELEASE_EXCL_LOCK();
214 }
215 return childPid;
216 }
217
218 extern "C" pid_t vfork()
219 {
220 JTRACE("vfork wrapper calling fork");
221 // This might not preserve the full semantics of vfork.
222 // Used for checkpointing gdb.
223 return fork();
224 }
225
226 // Special short-lived processes from executables like /lib/libc.so.6
227 // and man setuid/setgid executables cannot be loaded with LD_PRELOAD.
228 // Since they're short-lived, we execute them while holding a lock
229 // delaying checkpointing.
230 static void execShortLivedProcessAndExit(const char *path, char *const argv[])
231 {
232 unsetenv("LD_PRELOAD"); // /lib/ld.so won't let us preload if exec'ing lib
233 const unsigned int bufSize = 100000;
234 char *buf = (char*)JALLOC_HELPER_MALLOC(bufSize);
235 memset(buf, 0, bufSize);
236 FILE *output;
237 if (argv[0] == NULL) {
238 output = _real_popen(path, "r");
239 } else {
240 string command = path;
241 for (int i = 1; argv[i] != NULL; i++)
242 command = command + " " + argv[i];
243 output = _real_popen(command.c_str(), "r");
244 }
245 int numRead = fread(buf, 1, bufSize - 1, output);
246 numRead++, numRead--; // suppress unused-var warning
247
248 pclose(output); // /lib/libXXX process is now done; can checkpoint now
249 // FIXME: code currently allows wrapper to proceed without lock if
250 // it was busy because of a writer. The unlock will then fail below.
251 bool __wrapperExecutionLockAcquired = true; // needed for LOCK_UNLOCK macro
252 WRAPPER_EXECUTION_RELEASE_EXCL_LOCK();
253 // We are now the new /lib/libXXX process, and it's safe for DMTCP to ckpt us.
254 printf("%s", buf); // print buf, which is what /lib/libXXX would print
255 JALLOC_HELPER_FREE(buf);
256 // Avoid running exit handlers of the parent process by calling _exit.
257 _exit(0);
258 }
259
260 // FIXME: Unify this code with code prior to execvp in dmtcp_launch.cpp
261 // Can use argument to dmtcpPrepareForExec() or getenv("DMTCP_...")
262 // from DmtcpWorker constructor, to distinguish the two cases.
263 static void dmtcpPrepareForExec(const char *path, char *const argv[],
264 char **filename, char ***newArgv)
265 {
266 JTRACE("Preparing for Exec") (path);
267
268 const char * libPrefix = "/lib/lib";
269 const char * lib64Prefix = "/lib64/lib";
270 if (path != NULL && Util::strStartsWith(path, libPrefix))
271 execShortLivedProcessAndExit(path, argv);
272 if (path != NULL && Util::strStartsWith(path, lib64Prefix))
273 execShortLivedProcessAndExit(path, argv);
274 // Needed for /usr/libexec/utempter/utempter and other short-lived
275 // setuid/setgid processes.
276 // FIXME: USE THIS FOR ALL setuid/setgid PROCESSES EXCEPT ONES THAT
277 // WE DIRECTLY HANDLE, LIKE 'screen'. (Need to name special routine,
278 // execScreenProcess() ??)
279 if (path != NULL && Util::strEndsWith(path, "/utempter")) {
280 JTRACE("Trying to exec: utempter")(path)(argv[0])(argv[1]);
281 int oldIdx = -1;
282 char *oldStr = NULL;
283 string realPtsNameStr;
284 // utempter takes a pts slave name as an argument. Since we virtualize
285 // ptys, the slave name points to a virtual slave name, thus we need to
286 // replace it with the real one.
287 for (size_t i = 0; argv[i] != NULL; i++) {
288 if (Util::strStartsWith(argv[i], VIRT_PTS_PREFIX_STR)) {
289 // FIXME: Potential memory leak if exec() fails.
290 char *realPtsNameStr = (char*)JALLOC_HELPER_MALLOC(PTS_PATH_MAX);
291 oldStr = argv[i];
292 oldIdx = i;
293 SharedData::getRealPtyName(argv[i], realPtsNameStr,
294 PTS_PATH_MAX);
295 // Override const restriction
296 *(const char**)&argv[i] = realPtsNameStr;
297 }
298 }
299 execShortLivedProcessAndExit(path, argv);
300 if (oldIdx != -1) {
301 // Restore original argv[] if exec failed.
302 *(const char**)&argv[oldIdx] = oldStr;
303 }
304 }
305
306 // FIXME: SEE COMMENTS IN dmtcp_launch.cpp, rev. 1087; AND CHANGE THIS.
307 if (Util::isSetuid(path)) {
308 if (Util::isScreen(path)) {
309 Util::setScreenDir();
310 }
311 // THIS NEXT LINE IS DANGEROUS. MOST setuid PROGRAMS CAN'T RUN UNPRIVILEGED
312 Util::patchArgvIfSetuid(path, argv, newArgv);
313 // BUG: Util::patchArgvIfSetuid() DOES NOT SET newArgv WHEN COPYING
314 // BINARY IN CODE RE-FACTORING FROM REVISION 911.
315 *filename = (*newArgv)[0];
316 } else {
317 *filename = (char*)path;
318 *newArgv = (char**)argv;
319 }
320
321 ostringstream os;
322 os << dmtcp_get_tmpdir() << "/dmtcpLifeBoat." << UniquePid::ThisProcess()
323 << "-XXXXXX";
324 char *buf = (char*) JALLOC_HELPER_MALLOC(os.str().length()+1);
325 strcpy(buf, os.str().c_str());
326 int fd = _real_mkstemp(buf);
327 JASSERT(fd != -1) (JASSERT_ERRNO);
328 JASSERT(unlink(buf) == 0) (JASSERT_ERRNO);
329 Util::changeFd(fd, PROTECTED_LIFEBOAT_FD);
330 jalib::JBinarySerializeWriterRaw wr ("", PROTECTED_LIFEBOAT_FD);
331 UniquePid::serialize (wr);
332 DmtcpEventData_t edata;
333 edata.serializerInfo.fd = PROTECTED_LIFEBOAT_FD;
334 DmtcpWorker::eventHook(DMTCP_EVENT_PRE_EXEC, &edata);
335
336 JTRACE("Will exec filename instead of path") (path) (*filename);
337
338 Util::adjustRlimitStack();
339 Util::prepareDlsymWrapper();
340
341 // Remove FD_CLOEXEC flag from protected file descriptors.
342 for (size_t i = PROTECTED_FD_START; i < PROTECTED_FD_END; i++) {
343 int flags = fcntl(i, F_GETFD, NULL);
344 if (flags != -1) {
345 fcntl(i, F_SETFD, flags & ~FD_CLOEXEC);
346 }
347 }
348 JTRACE("Prepared for Exec") (getenv("LD_PRELOAD"));
349 }
350
351 static void dmtcpProcessFailedExec(const char *path, char *newArgv[])
352 {
353 int saved_errno = errno;
354
355 if (Util::isSetuid(path)) {
356 Util::freePatchedArgv(newArgv);
357 }
358
359 restoreUserLDPRELOAD();
360
361 unsetenv(ENV_VAR_DLSYM_OFFSET);
362 unsetenv(ENV_VAR_DLSYM_OFFSET_M32);
363
364 JTRACE("Processed failed Exec Attempt") (path) (getenv("LD_PRELOAD"));
365 errno = saved_errno;
366 JASSERT(_real_close(PROTECTED_LIFEBOAT_FD) == 0) (JASSERT_ERRNO);
367 }
368
369 static string getUpdatedLdPreload(const char* filename,
370 const char* currLdPreload)
371 {
372 string preload = getenv(ENV_VAR_HIJACK_LIBS);
373
374 bool isElf = false;
375 bool is32bitElf = false;
376 if (getenv(ENV_VAR_HIJACK_LIBS_M32) != NULL &&
377 Util::elfType(filename, &isElf, &is32bitElf) != -1 &&
378 isElf &&
379 is32bitElf) {
380 preload = getenv(ENV_VAR_HIJACK_LIBS_M32);
381 }
382
383 vector<string> pluginLibraries = Util::tokenizeString(preload, ":");
384 for (size_t i = 0; i < pluginLibraries.size(); i++) {
385 // If the plugin doesn't exist, try to search it in the current install
386 // directory.
387 if (!jalib::Filesystem::FileExists(pluginLibraries[i])) {
388 pluginLibraries[i] =
389 Util::getPath(jalib::Filesystem::BaseName(pluginLibraries[i]),
390 is32bitElf);
391 }
392 }
393
394 const char *preloadEnv = getenv("LD_PRELOAD");
395 if (currLdPreload != NULL && strlen(currLdPreload) > 0) {
396 pluginLibraries.push_back(currLdPreload);
397 setenv(ENV_VAR_ORIG_LD_PRELOAD, currLdPreload, 1);
398 } else if (preloadEnv != NULL && strlen(preloadEnv) > 0) {
399 pluginLibraries.push_back(preloadEnv);
400 setenv(ENV_VAR_ORIG_LD_PRELOAD, preloadEnv, 1);
401 }
402
403 string newPreload = Util::joinStrings(pluginLibraries, ":");
404 return newPreload;
405 }
406
407 static vector<string> copyEnv(char *const envp[])
408 {
409 vector<string> result;
410 for (size_t i = 0; envp[i] != NULL; i++) {
411 result.push_back(envp[i]);
412 }
413 return result;
414 }
415
416 static vector<const char*> stringVectorToPointerArray(const vector<string>& s)
417 {
418 vector<const char*> result;
419 // Now get the pointers.
420 for (size_t i = 0; i < s.size(); i++) {
421 result.push_back(s[i].c_str());
422 }
423 result.push_back(NULL);
424 return result;
425 }
426
427 static const char* ourImportantEnvs[] =
428 {
429 ENV_VARS_ALL //expands to a long list
430 };
431 #define ourImportantEnvsCnt ((sizeof(ourImportantEnvs))/(sizeof(const char*)))
432
433 static bool isImportantEnv (string str)
434 {
435 str = str.substr(0, str.find("="));
436
437 for (size_t i=0; i<ourImportantEnvsCnt; ++i) {
438 if (str == ourImportantEnvs[i])
439 return true;
440 }
441 return false;
442 }
443
444 static vector<string> patchUserEnv (vector<string> env, const char* filename)
445 {
446 vector<string> result;
447 string userPreloadStr;
448
449 ostringstream out;
450 out << "non-DMTCP env vars:\n";
451
452 for (size_t i = 0; i < env.size(); i++) {
453 if (isImportantEnv (env[i])) {
454 if (dbg) {
455 out << " skipping: " << env[i] << '\n';
456 }
457 continue;
458 }
459 if (Util::strStartsWith(env[i], "LD_PRELOAD=")) {
460 userPreloadStr = env[i].substr(strlen("LD_PRELOAD="));
461 continue;
462 }
463
464 result.push_back(env[i]);
465 if (dbg) {
466 out << " addenv[user]:" << result.back() << '\n';
467 }
468 }
469 JTRACE("Creating a copy of (non-DMTCP) user env vars...") (out.str());
470
471 //pack up our ENV into the new ENV
472 out.str("DMTCP env vars:\n");
473 for (size_t i=0; i<ourImportantEnvsCnt; ++i) {
474 const char* v = getenv(ourImportantEnvs[i]);
475 const string e = ourImportantEnvs[i];
476 if (e == ENV_VAR_ORIG_LD_PRELOAD && !userPreloadStr.empty()) {
477 result.push_back(e + "=" + userPreloadStr);
478 } else if (v != NULL) {
479 result.push_back (e + '=' + v);
480 if (dbg) {
481 out << " addenv[dmtcp]:" << result.back() << '\n';
482 }
483 }
484 }
485
486 string ldPreloadStr = "LD_PRELOAD=";
487 ldPreloadStr += getUpdatedLdPreload(filename, userPreloadStr.c_str());
488
489 result.push_back(ldPreloadStr);
490 if (dbg) {
491 out << " addenv[dmtcp]:" << result.back() << '\n';
492 }
493
494 JTRACE("Patched user envp...") (out.str());
495
496 return result;
497 }
498
499 extern "C" int execve (const char *filename, char *const argv[],
500 char *const envp[])
501 {
502
503 if (isPerformingCkptRestart() || isBlacklistedProgram(filename) ) {
504 return _real_execve(filename, argv, envp);
505 }
506 JTRACE("execve() wrapper") (filename);
507
508 /* Acquire the wrapperExeution lock to prevent checkpoint to happen while
509 * processing this system call.
510 */
511 WRAPPER_EXECUTION_GET_EXCL_LOCK();
512
513 const vector<string> env = copyEnv(envp);
514
515 char *newFilename;
516 char **newArgv;
517 dmtcpPrepareForExec(filename, argv, &newFilename, &newArgv);
518
519 const vector<string> envStrings = patchUserEnv(env, filename);
520 const vector<const char*> newEnv = stringVectorToPointerArray(envStrings);
521
522 int retVal = _real_execve (newFilename, newArgv, (char* const*)&newEnv[0]);
523
524 dmtcpProcessFailedExec(filename, newArgv);
525
526 WRAPPER_EXECUTION_RELEASE_EXCL_LOCK();
527
528 return retVal;
529 }
530
531 extern "C" int execv (const char *path, char *const argv[])
532 {
533 JTRACE("execv() wrapper, calling execve with environ") (path);
534
535 // Make a copy of the environ coz it might change after a setenv().
536 const vector<string> envStrings = copyEnv(environ);
537 // Now get the pointers.
538 const vector<const char*> env = stringVectorToPointerArray(envStrings);
539
540 return execve(path, argv, (char* const*) &env[0]);
541 }
542
543 extern "C" int execvp (const char *filename, char *const argv[])
544 {
545 if (isPerformingCkptRestart() || isBlacklistedProgram(filename) ) {
546 return _real_execvp(filename, argv);
547 }
548 JTRACE("execvp() wrapper") (filename);
549 /* Acquire the wrapperExeution lock to prevent checkpoint to happen while
550 * processing this system call.
551 */
552 WRAPPER_EXECUTION_GET_EXCL_LOCK();
553
554 char *newFilename;
555 char **newArgv;
556 dmtcpPrepareForExec(filename, argv, &newFilename, &newArgv);
557 setenv("LD_PRELOAD", getUpdatedLdPreload(filename, NULL).c_str(), 1);
558
559 int retVal = _real_execvp (newFilename, newArgv);
560
561 dmtcpProcessFailedExec(filename, newArgv);
562
563 WRAPPER_EXECUTION_RELEASE_EXCL_LOCK();
564
565 return retVal;
566 }
567
568 // This function first appeared in glibc 2.11
569 extern "C" int execvpe (const char *filename, char *const argv[],
570 char *const envp[])
571 {
572 if (isPerformingCkptRestart() || isBlacklistedProgram(filename)) {
573 return _real_execvpe(filename, argv, envp);
574 }
575 JTRACE("execvpe() wrapper") (filename);
576 /* Acquire the wrapperExeution lock to prevent checkpoint to happen while
577 * processing this system call.
578 */
579 WRAPPER_EXECUTION_GET_EXCL_LOCK();
580
581 // Make a copy of the environ coz it might change after a setenv().
582 const vector<string> env = copyEnv(envp);
583
584 char *newFilename;
585 char **newArgv;
586 dmtcpPrepareForExec(filename, argv, &newFilename, &newArgv);
587
588 const vector<string> newEnvStrings = patchUserEnv(env, filename);
589 const vector<const char*> newEnv = stringVectorToPointerArray(newEnvStrings);
590
591 int retVal = _real_execvpe(newFilename, newArgv, (char* const*)&newEnv[0]);
592
593 dmtcpProcessFailedExec(filename, newArgv);
594
595 WRAPPER_EXECUTION_RELEASE_EXCL_LOCK();
596
597 return retVal;
598 }
599
600 extern "C" int fexecve (int fd, char *const argv[], char *const envp[])
601 {
602 char buf[sizeof "/proc/self/fd/" + sizeof (int) * 3];
603 snprintf (buf, sizeof (buf), "/proc/self/fd/%d", fd);
604
605 JTRACE("fexecve() wrapper calling execve()") (fd) (buf);
606 return execve(buf, argv, envp);
607 }
608
609
610 extern "C" int execl (const char *path, const char *arg, ...)
611 {
612 JTRACE("execl() wrapper") (path);
613
614 size_t argv_max = INITIAL_ARGV_MAX;
615 const char *initial_argv[INITIAL_ARGV_MAX];
616 const char **argv = initial_argv;
617 va_list args;
618
619 argv[0] = arg;
620
621 va_start (args, arg);
622 unsigned int i = 0;
623 while (argv[i++] != NULL)
624 {
625 if (i == argv_max)
626 {
627 argv_max *= 2;
628 const char **nptr = (const char**) realloc (argv == initial_argv ? NULL : argv,
629 argv_max * sizeof (const char *));
630 if (nptr == NULL)
631 {
632 if (argv != initial_argv)
633 free (argv);
634 return -1;
635 }
636 if (argv == initial_argv)
637 /* We have to copy the already filled-in data ourselves. */
638 memcpy (nptr, argv, i * sizeof (const char *));
639
640 argv = nptr;
641 }
642
643 argv[i] = va_arg (args, const char *);
644 }
645 va_end (args);
646
647 int ret = execv (path, (char *const *) argv);
648 if (argv != initial_argv)
649 free (argv);
650
651 return ret;
652 }
653
654
655 extern "C" int execlp (const char *file, const char *arg, ...)
656 {
657 JTRACE("execlp() wrapper") (file);
658
659 size_t argv_max = INITIAL_ARGV_MAX;
660 const char *initial_argv[INITIAL_ARGV_MAX];
661 const char **argv = initial_argv;
662 va_list args;
663
664 argv[0] = arg;
665
666 va_start (args, arg);
667 unsigned int i = 0;
668 while (argv[i++] != NULL)
669 {
670 if (i == argv_max)
671 {
672 argv_max *= 2;
673 const char **nptr = (const char**) realloc (argv == initial_argv ? NULL : argv,
674 argv_max * sizeof (const char *));
675 if (nptr == NULL)
676 {
677 if (argv != initial_argv)
678 free (argv);
679 return -1;
680 }
681 if (argv == initial_argv)
682 /* We have to copy the already filled-in data ourselves. */
683 memcpy (nptr, argv, i * sizeof (const char *));
684
685 argv = nptr;
686 }
687
688 argv[i] = va_arg (args, const char *);
689 }
690 va_end (args);
691
692 int ret = execvp (file, (char *const *) argv);
693 if (argv != initial_argv)
694 free (argv);
695
696 return ret;
697 }
698
699
700 extern "C" int execle(const char *path, const char *arg, ...)
701 {
702 JTRACE("execle() wrapper") (path);
703
704 size_t argv_max = INITIAL_ARGV_MAX;
705 const char *initial_argv[INITIAL_ARGV_MAX];
706 const char **argv = initial_argv;
707 va_list args;
708 argv[0] = arg;
709
710 va_start (args, arg);
711 unsigned int i = 0;
712 while (argv[i++] != NULL)
713 {
714 if (i == argv_max)
715 {
716 argv_max *= 2;
717 const char **nptr = (const char**) realloc (argv == initial_argv ? NULL : argv,
718 argv_max * sizeof (const char *));
719 if (nptr == NULL)
720 {
721 if (argv != initial_argv)
722 free (argv);
723 return -1;
724 }
725 if (argv == initial_argv)
726 /* We have to copy the already filled-in data ourselves. */
727 memcpy (nptr, argv, i * sizeof (const char *));
728
729 argv = nptr;
730 }
731
732 argv[i] = va_arg (args, const char *);
733 }
734
735 const char *const *envp = va_arg (args, const char *const *);
736 va_end (args);
737
738 int ret = execve (path, (char *const *) argv, (char *const *) envp);
739 if (argv != initial_argv)
740 free (argv);
741
742 return ret;
743 }
744
745 // See comment in glibcsystem.cpp for why this exists and how it works.
746 extern int do_system (const char *line);
747
748 extern "C" int system (const char *line)
749 {
750 JTRACE("before system(), checkpointing may not work")
751 (line) (getenv (ENV_VAR_HIJACK_LIBS)) (getenv ("LD_PRELOAD"));
752
753 if (line == NULL)
754 /* Check that we have a command processor available. It might
755 not be available after a chroot(), for example. */
756 return do_system ("exit 0") == 0;
757
758 int result = do_system (line);
759
760 JTRACE("after system()");
761
762 return result;
763 }