How to not propagate environmental variables to command of a child process? - c

I am trying to understand the specifications of a C coding project using fork() and execv().
Essentially, I am told I need to either propagate or not propagate the environment variables of the shell to the command I am executing in a child process depending on a binary value.
Though, regardless, I am supposed to pass in any additional environmental variables I am provided to the command I am executing in the child.
This whole idea is new information to me, so I am just trying to understand what exactly this means.
To my understanding, when a child is created using fork(), the environmental variables of the parent process are passed on to the child, and then any additional environmental variable changes can be made as an argument of the execve() function when it is run within the child.
But if this is the case, then the environmental variables are always propagated from the parent/shell anyway right? So what is the difference and how would I not propagate those variables?
Thanks for your time.

You're still in control after the fork() and before the execv().
You can make changes to your child's environment that you don't want to make to your parent -- including calls to unsetenv() -- in that process.
Alternately, you can construct a new environment entirely and pass it in the envp argument to execve().

If you understood fork(2) then you'll know that the parent and the child are exactly the same (except for the value returned by fork()), so the environment of the parent process is exactly the same as the environment of the child. there's no other way to pass environment to a child process... because execve(2) doesn't create a new process. You only load the process' virtual space by loading a new program on it. And this allows you to change the environment, as it happens with the argument list. Think of argc and argv as some way of passing positional parameters to a program, and the environment as some way to pass named parameters to a program. Both are exactly the same kind of things, but ones are seek by position, while the others are accessed by name.
But no new process is created on execve(2) (nor in any of its variants)

Related

Creating a child process WITHOUT fork()

Is there a way to start a child process without fork(), using execvp() exclusively?
The pedantic answer to your question is no. The only system call that creates a new process is fork. The system call underlying execvp (called execve) loads a new program into an existing process, which is a different thing.
Some species of Unix have additional system calls besides fork (e.g. vfork, rfork, clone) that create a new process, but they are only small variations on fork itself, and none of them are part of the POSIX standard that specifies the functionality you can count on on anything that calls itself a Unix.
The slightly more helpful answer is that you might be looking for posix_spawn, which is a library routine wrapping fork and exec into a single operation, but I find it more troublesome to use that correctly than to write my own fork+exec subroutine. YMMV.
posix_spawn is the only posix compliant way to create a child process without calling fork directly. I say 'directly' because historically posix_spawn would itself just call fork, or vfork. However, that is no longer the case in GNU/linux. posix_spawn itself may be more efficient than fork, in addition to perhaps being a stronger fit conceptually when code is attempting to run a different executable.
If you aren't worried about portability, you can abandon posix and couple yourself directly to the kernel you are targeting. On linux the system call to create a child process is clone. At the time of this answer the manual page provides documentation for three variants, including the relatively new clone3.
I believe you can take the example from the manual page and add an execvp call to childFunc. I have not tried it yet, though!
Unlike Windows systems, where creating a new process and executing a new process image happen in a single step, Linux and other UNIX-like systems do them as two distinct steps.
The fork function makes an exact duplicate of the calling process and actually returns twice, once to the parent process and once to the child process. The execvp function (and other functions in the exec family) executes a new process image in the same process, overwriting the existing process image.
You can call execvp without calling fork first. If so, that just means the currently running program goes away and is replaced with the given program. However, fork is the way to create a new process.
As user zwol has already explained, execve() does not fork a new process. Rather, it replaces the address space and CPU state of current process,
loads the new address space from the executable filename and starts it from
main() with argument list argv and environment variable list envp.
It keeps pid and open files.
int execve(const char *filename,char *const argv [],char *const envp[]);
filename: name of executable file to run
argv: Command line arguments
envp: environment variable settings (e.g., $PATH, $HOME, etc.)
posix_spawn. But it ignores failures of execvp() -- potentially because implementing this was regarded as too complicated.

Reading global environment variables

I have a process which is created by execve() from another program and a specific set of environment is passed to it. However, I would need to read a global environment variable that may or may not be set by the user based on certain aspects. For example, $PATH is set when the execve() is called, but if the user stes $LOWMEM=1 (export LOWMEM=1)
before the program is exec'ed, then the program will not make malloc() calls larger than 1KB.
when I do a getenv("LOWMEM") call from the program it returns NULL where as I have added LOWMEM=1 in /etc/enviroment and echo $LOWMWM shows the value as 1. But I don't see LOWMEM entry in /proc/<PID>/environ. Whereas I had rebooted the system after adding the entry to the /etc/environment.
Is it not possible to read global environment from a process running under specific environment without explicitly passing it from the parent process?
It seems most likely that you're clearing out the entry for LOWMEM while using execve. Instead, it might be better to call setenv for the new variables you want, either in the parent or in the child and then call execv.

Running a new child process in background C linux

I am trying to run a new process in background so it will be possible to continue working with parent process.
I used fork then execl. I tried to add to the execl command the argument & but it doesn't work:
execl("newproc","newproc","arg1","&",NULL);
Is there any solution?
The child will run in the background since you used fork. The child will keep running in parallel with the parent (if exec succeeded). If you care about whether the child process succeeded or not (and your code should) you should eventually call waitpid to collect its exit status. Otherwise you should call fork twice and have the intermediate process exit without waiting for the child, so that init adopts the grandchild process.
As #mah said, the & is unnecessary. But another change is needed to that line; execl is a variadic function, and function prototypes therefore don't take care of converting arguments to the correct type. Therefore the final argument should be passed as the correct type - just change it to (char*)NULL.
You mention that your code didn't work. While that could just be because of the spurious &, it may also be because of the first argument. The execl function does not search $PATH for the named program, so unless newproc is actually in the current directory, this execl() invocation will return. When execl returns, that always indicates that there is a problem. The simplest way to solve this is to use execlp() instead of execl(). The alternative approach is to specify an absolute path as the first argument. You can even specify a relative path as the first argument, but this is rarely useful.
The problem is that & is not a command line option to programs. Instead, it is merely special shell syntax which puts a command in the background. The distinguishing feature of background programs is that they are not connected to a terminal, and the terminal is not waiting for the process to complete. The proper function is daemon(). Do a man daemon to read up on how it is used.
& is not a command argument, its a flag that the shell uses to know to run the command in the background. In this case, you're performing the work of the shell... remove the &. Since you state you've called fork(), as long as you're only performing execl() in the child process after fork returns, you're already running in the background.

Process tree, how to find if a said process is the root one?

I have a directory monitoring application which works recursively by launching new processes.
I'd like to know if I'm the "root" process in this tree. I thought about trying to get the name of the caller process and check if it's the same as argv[0].
Is there a smarter way of doing this? Keep in mind, this is a Linux app.
Keep in mind, I don't have much time for this and I'm but a student, so a simple solution would be great.
Thanks for your time.
If you use fork() to create new processes, you can have a local variable initially set at zero that each child sets to 1 immediately after forking. Only the root process would still have it set at zero after a fork.
You could even increase it after each fork, which would let you know how deep in your process tree each process is.
EDIT:
If you cannot use this (e.g. because you do an exec() after fork), you can use any of the common ways that shells use to pass information to the programs that you launch:
Environment variables: call setenv() after fork() but before exec() - or add it in the environment when calling exec().
Use a special command line argument.
Use a special value for argv[0] when doing exec().
Have you the possibility to add an argument meaning "I'm not the root"? That seems the simplest approach.
If you are calling exec, add a special argument, or environment variable called "I_AM_NOT_THE_ROOT" which the child processes get, but the parent does not.
I recently used a command-line argument for this, but env variables might be more convenient.

In a POSIX environment, how do I track files accessed by a child process?

I have my own POSIX application which starts a child process. I want the parent process to be notified with the names of all files the child process reads or writes, as well as the file names of any child processes the child spawns, and any dynamic libraries it loads. Similarly, I need to monitor all child processes spawned by child processes, etc.
How is this done?
I have two ideas for this.
Method 1 - The "real way".
I think you want ptrace. But it isn't going to be easy to use.
Essentially this call is for writing a debugger. Note that PTRACE_SYSCALL steps until the next syscall. At which point you might be able to use more ptrace calls to peek at the process's memory to observe if it's, say, a call to open().
Method 2 - The lazy, hackish way.
You could use the LD_PRELOAD environment variable. That is, write a shared library with your own implementation of the calls you want to hook (say, open(), dlopen()), adding your own code and dispatching to the normal libc version. Then you point the LD_PRELOAD environment variable at this shared library so the dynamic linker will load it at process start.
One downside to this approach is that if a process knows it's being observed this way, it can reset the environment variable and execute itself again, and evade detection. Another I can think of is that as a security feature this environment variable is not honored if you're root.

Resources