Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get environment variables using C code

Tags:

c

linux

embedded

Here I wrote a C program which executes hi.sh file using system call.

Here I used . ./hi.sh so I want to execute this script in the same shell and then try to get environment variable using getenv function, but here I am getting different output from what I expected.

The hi.sh file contains

export TEST=10
return

Means when I run this hi.sh file using system call, its export TEST sets the value to 10 in same shell. After this, I am trying to get this variable value but its given NULL value.

And if I run this script manually from console like . ./hi.sh then it works fine and I get 10 value of TEST using getenv("TEST") function.

Code:

#include <stdio.h>
int main()
{
    system(". ./hi.sh");
    char *errcode;
    char *env = "TEST";
    int errCode;    
    errcode = getenv(env);
    printf("Value is = %s\n",errcode);
    if (errcode != NULL) {
        errCode =atoi(errcode);
        printf("Value is = %d\n",errCode);
    }
}

output :

Value is = (null)

How can I export TEST variable in program shell? If system() executes commands in different shell then how can I use C program code to get an environment variable which is exported by the shell invoked via a system() call?

like image 258
user1089679 Avatar asked Apr 03 '12 09:04

user1089679


3 Answers

Another possible solution is to have your program exec itself through another shell. That shell replace the running program, then read the environment variables and then replace shell with a new copy of the program. You need to tell the new copy that it has already done an exec or it will just loop doing it over and over. You could look for the environment variable, or pass a command-line flag.

An untested example:

execl("/bin/sh", "-c", ". ./hi.sh; exec ./a.out --envset", NULL);

You would need to replace a.out with whatever the real program name is. You would probably want to extract it from argv[0] and also pass the rest of the argv array. But you have to reformat the arguments to work as shell arguments, so they need to be quoted as necessary, etc.

like image 150
Zan Lynx Avatar answered Nov 08 '22 17:11

Zan Lynx


The child process cannot directly set the parent process's environment. The approach using system() and getenv() is doomed to fail, therefore.

If you are trying to import selected variables set by the script hi.sh, then you have a couple of choices. Either you can read the script hi.sh and work out what it would set them to (rather hard), or you can run the script and have the code you run report back on the environment variables of interest.

Suppose that hi.sh sets $ENV1 and $ENV2. You can use popen() to get the values back to your program, and setenv() to set your program's environment. In outline:

FILE *fp = popen(". ./hi.sh; echo ENV1=$ENV1; echo ENV2=$ENV2", "r");

while (fgets(buffer, sizeof(buffer), fp) != 0)
{
    ...split the buffer into env_name, env_value...
    setenv(env_name, env_value);
}

pclose(fp);

Note that I included the variable name in the echoed information; this simplifies life. If your list of variables gets unwieldy, maybe you run ". ./hi.sh; env" to get the entire environment, and then read each line and work out from your built-in list whether its a variable setting you want to use or not. Or you can simply set your entire environment again, if that pleases you. You should check that the setenv() function succeeded (it returns zero when it does succeed). You should also check that the popen() was successful (fp != 0). In this context, you probably can use strtok() to look for the = separating the variable name from the value; it tramples a null byte over the =, giving you a null terminated name and a null terminated value:

    char *env_name = strtok(buffer, "=");
    char *env_value = buffer + strlen(env_name) + 1;
    if (setenv(env_name, env_value) != 0)
        ...report trouble...
like image 35
Jonathan Leffler Avatar answered Nov 08 '22 16:11

Jonathan Leffler


As usual, the man page does explain this, but you need to read it very carefully.

DESCRIPTION
       system()  executes a command specified in command by calling /bin/sh -c
       command, and returns after the command has been completed.  During exe‐
       cution  of the command, SIGCHLD will be blocked, and SIGINT and SIGQUIT
       will be ignored.

In other words, system() first starts /bin/sh, and then has /bin/sh start whatever command you want to execute. So what happens here is that the TEST variable is exported to the /bin/sh shell the system() call implicitly started, but not to the program which called system().

like image 6
Kristof Provost Avatar answered Nov 08 '22 16:11

Kristof Provost