Environment variables in DMTCP
This is intended as one of a series of informal documents to describe
and partially document some of the more subtle DMTCP data structures
and algorithms. These documents are snapshots in time, and they
may become somewhat out-of-date over time (and hopefully also refreshed
to re-sync them with the code again).
This document is about the environment variables usually accessed through
the system calls getenv, setenv, putenv, etc. Environment variables
for a process are initialized by the kernel as part of execve().
Even though glibc (and POSIX) provide for execl, execv, execle, execve,
execlp, execvp, and fexecve, all of these are translated by glibc to execve
when calling the kernel. This is why only execve has an additional man page
in section 2 ('man 2 execve').
After initialization, environment variables are maintained solely in
user space, and are ignored by the kernel. Since DMTCP saves all of
user space in a checkpoint image, all environment variable are saved
at checkpoint time and restored exactly on restart. This is a good
thing. For example, a shell program may cache its own copy of the environment
variables prior to checkpoint. It would be bad if environment variables
asynchronously appeared and disappeared as part of checkpoint/restart.
Most environment variable are defined as C macros in dmtcp/src/constants.h.
#define ENV_VAR_QUIET "DMTCP_QUIET"
In addition to user environment variables, DMTCP sets a limited number
of its own environment variables at the time of exec. Those environment
variables are mostly unset before the user's main() function begins.
There are some special cases. Variables like DMTCP_TMPDIR, DMTCP_QUIET,
and LD_PRELOAD must be passed across a user's call to exec or to "ssh".
This is because DMTCP is intended to be "contagious". As new processes
are exec'ed, they must inherit the DMTCP checkpointing software and the
environment variables used to configure them.
As of the time of this writing, the DMTCP wrapper for execv() calls
the wrapper for execve(). The DMTCP wrapper for execvp() (and execlp, which
delegates to execvp) has its own code, distinct from execve. (WHY?)
The execvp wrapper adjusts LD_PRELOAD to add our libdmtcp.so
to LD_PRELOAD. When the process exec's, it will load and execute
libdmtcp.so before the user's main() routine. That code will remove
libdmtcp.so from LD_PRELOAD so as to hide the modified LD_PRELOAD from
the user code. See dmtcpworker.cpp:restoreUserLDPRELOAD() for that code.
The restoreUserLDPRELAD code is also invoked for a newly loaded program
invoked via dmtcp_launch.
The execve wrapper should also be doing this. In fact, this code
should be absorbed into execwrappers.cpp:prepareForExec(). IS THIS DONE?
IF NOT, WHY NOT?)
The execve wrapper calls patchUserEnv, which removes any DMTCP environment
variables. In principle, there shouldn't be any remaining DMTCP
environment variables, and this code is not needed. We should have
removed any DMTCP environment variables at the beginning of the call
to libdmtcp.so before the user's main() function is executed.
(LET'S TEST THIS, AND TURN THIS patchUserEnv INTO AN EXTENDED ASSERTION
TO CHECK FOR CORRECTNESS.)