Operating System - HP-UX
1820389 Members
3544 Online
109623 Solutions
New Discussion юеВ

Setting ENVIRONMENT variable from C program

 
oradba
Occasional Contributor

Setting ENVIRONMENT variable from C program

Is there a way to change a Shell environment variable from inside a C program.

When I use PUTENV inside a C program, the new value is visible by using GETENV. But when the executable program ends and I am back to shell prompt, the new value is not visible.
13 REPLIES 13
A. Clay Stephenson
Acclaimed Contributor

Re: Setting ENVIRONMENT variable from C program

By design, there is absolutely no way to do this in C (or anything else in UNIX). A child process can inherit the environment of a parent process but the converse is never true.

If it ain't broke, I can fix that.
Shannon Petry
Honored Contributor

Re: Setting ENVIRONMENT variable from C program

Not unless you have a program that just sets variables that you can source. The source syntax would be as follows.

KSH/BSH/POSIX

. /dir/yourcode

CSH

source /dir/yourcode

Regards,
Shannon
Microsoft. When do you want a virus today?
John Palmer
Honored Contributor

Re: Setting ENVIRONMENT variable from C program

Only with the cooperation of the parent shell. There are several techniques that could be employed such as...

=$(
- writes subshell to assign values to variables
.
rm

etc. etc.

Regards,
John
jp
Frequent Advisor

Re: Setting ENVIRONMENT variable from C program

""" BY DESIGN""" ????????

Only a very opinionated person would 'design' such a feature/restriction IMHO. Why is it so easy in MPE to do things like this - because its USEFUL!!!! If you have a child process that changes the environment, if necessary, its his job to restore it to the old state.
GRRRRRRRRRRRRRRRRRRRRRRRRRRRR.
jp
jp
Frequent Advisor

Re: Setting ENVIRONMENT variable from C program

I now have a case where I start with a visible variable VAR1=VALUE1 in my session, exported etc, then I run a program which does a system("env") (can see VAR1 here) issues some new putenv calls (no errors), including one replacing VAR1, then does system("env"). Now cant see VAR1 at all, until program ends, when I can see VAR1 again.
Anybody know whats going on here please?? is there something special I am not doing in my putenv? I am terminating the string with a \r and then null.
TIA,
jp
jp
Frequent Advisor

Re: Setting ENVIRONMENT variable from C program

Finally got this licked! By carefully reading the manual, I found the problem. putenv() puts a pointer to the string its given in the environment. If the string passed by putenv is automatic space (ie not static), the moment that space is lost in the program, the environment ptr is invalid, and its value is lost. Thats the way I read it anyway. man putenv says this is a 'potential error'...!!
So it seems to me that if a child process calls getenv on one of these variables, the ptr it gets back is actually a ptr to static space in the parent!!! Isnt that a security breach? what if the parent stack contains a list of passwords? COuldnt a rogue child find these by playing with the ptr?
GRRRRRRRRRRRRRRRRRRRRR again.
jp
Mike Stroyan
Honored Contributor

Re: Setting ENVIRONMENT variable from C program

When the putenv manual page refers to "the environment" it always means the set of environment variables of the current process. There is no 'system environment' or 'account environment' or 'session environment'. Each process has its own set of environment variables. A program chooses what environment variables to pass on when it starts a child process with fork() and exec() calls. (There are actually several forms of exec calls that can pass different environment variable data to the new program being started.)

The warning in the putenv manual is about the use of a stack variable like this-

void setnum(int num)
{
char v[40];
sprintf(v, "NUM=%d", num);
putenv(v);
}

There is no possibility that this would cause the environment to contain anything from another process. It will cause the environment of this process to point to the address of variable v. That stack address is likely be reused by other function calls. The stack pointer moves up and down as the process calls functions and returns from functions. The stack holds parameters, return pointers, saved registers, and local variables. The manual is warning that putenv does not make a copy of the string passed in. You need to make sure that string will remain valid. (That could be done with strdup(v)).
jp
Frequent Advisor

Re: Setting ENVIRONMENT variable from C program

"there is no possibility that the environment can point to another process' data" ....how can this be true if the variables are lost if the process overwrites the data used in a putenv(), which is what I think I am seeing? This is consistent with the warning against stack data usage. I will see if I can test seeing parent data I am not supposed to see in a child process.....
jp
jp
Frequent Advisor

Re: Setting ENVIRONMENT variable from C program

Let me clarify a bit. If PID 1 does a putenv, with data in static space, and a child of pid 1 does a getenv() on a variable it has inherited, the way I read it is that the ptr returned points at the static space of PID 1. If true, this means the child has access to the parent's data space?
YES, or NO?
jp
A. Clay Stephenson
Acclaimed Contributor

Re: Setting ENVIRONMENT variable from C program

No, you are wrong again. What the warning is all about is making sure that the putenv()'ed value points to a valid address space.
This means that putenv() must use either a static variable (as opposed to the default "auto" storage class) when declared within a function. The difference is that a static storage class variable retains its value after the function has gone out of scope. You could also safely use a global variable whether declared "static" or not because in this context "static" does not refer to storage class but rather whether or not a symbol is visible to other modules for linking purposes.

The only way to "out-bushwhack" this in a child process is the deliberate misuse of the vfork() system call --- and that doesn't work in many flavors of UNIX these days and is guaranteed to be less than portable.

If you are really interested in this topic, you should probably start a new thread.
If it ain't broke, I can fix that.
Mike Stroyan
Honored Contributor

Re: Setting ENVIRONMENT variable from C program

There are two ways to create a child process in unix based systems. The simple and original way is to call fork(). That will create a second process that is a copy of the first. All of the process' private address space is copied from the parent. All the normal data and stack are copies. Changes to them in one process to not affect the other process. Only explicit shared memory segments continue to be shared between a parent an child process. (Those are discussed in the man pages for shmget and mmap.)

There is a variation of fork called vfork. It takes a shortcut by not copying all the private data. It requires that a process call exec before the parent process can run again. The intention is that using vfork correctly will be the same as using fork, but a little faster.

The exec call causes an existing process id to load a completely different program. Exec will get rid of all memory regions in the process. They are replaced by the text, data, and stack regions of a new process. There are several versions of exec that take different parameters. They vary in how the new program file is found, how arguments are passed, and how the environment variables are passed on. All versions of exec will either explicity or implicitly pass on a particular set of environment variables that the new program will use. Those are copied from the process' old address space into its new one.

Let's look at your question about calling putenv() in a parent process and getenv() in a child process. The putenv call will modify the environment variable list in the current process. It will have no effect on already existing child processes. Getenv in other processes will continue to return the old value.

Calling fork to create a new process will make a child that inherits copies of all memory in the parent, including the environment strings. Calling execl() or execlp() or execv() or execvp() will copy the current environment variables into new addresses in the new program image that is started in the same process. Calling execle() or execve() will change the environment of the process to be copies of the string values passed in as parameters to that call.

So, a getenv call returns either NULL or a pointer to an address in the process that calls getenv. That address may have been set up by an exec call, a fork call, or a putenv call.
A. Clay Stephenson
Acclaimed Contributor

Re: Setting ENVIRONMENT variable from C program

The case where vfork()'ed child could alter data in the parent process occurred when the vfork()'ed child did not exec(). This misuse of vfork() in some of the BSD varients turned out to be a rather sneaky way to do what amounted to shared memory without using shared memory. Vfork() was intended to be a faster fork() and it was assumed that any vfork() would immediately be followed by an exec(). This loophole has largely been closed now and in many flavors of UNIX, vfork() is really a fork() to prevent exactly this sort of abuse.
If it ain't broke, I can fix that.
Elmar P. Kolkman
Honored Contributor

Re: Setting ENVIRONMENT variable from C program

If you want your C program to run and change a variable value, you can do it... Just output the variable change and evaluate the output in your shell script.

C program

void main()
{
puts("VAR1=newvalue");
}

Shell script:
echo "Before: $VAR1"
eval `a.out`
echo "After: $VAR1"
Every problem has at least one solution. Only some solutions are harder to find.