In C, how to create and send shell variable? - c

I would like to get the following result, let me explain, we have a file "test.sh", in which we call a command written in c (mycmd.c), and we assign it an argument. We want it to create a variable whose name is the argument passed and then return the result in the "test.sh" file.
Example :
In the file test.sh, we call our command, we assign as argument the name of the variable to create, then we display the extracted result with an echo.
#!/bin/bash
./mycmd myvar
echo "OK : $myvar"
In the mycmd.c file, we create a shell variable named myvar (the passed argument), then we assign a value to this variable (myvar = foo), and then we return it to the script "test.sh", who returns so "OK : foo".
It's possible ? Thanks.

What you request cannot be done. Every program you run, including shells and subshells, gets its own environment. By default, the contents of that environment is a copy of that of its parent process, but it is separate and independent. A process can modify only its own environment, not that of any other process.

I was going to suggest you use setenv, but after some thinking and testing, it is in fact not possible to do what you want with an arbitrary compiled program. Process environments are protected, and your program gets a new process and therefore a new environment when it runs.
The best way to do this would be to have your program output some shell commands to run. So if your program were the following
#include <stdlib.h>
#include <stdio.h>
int main() {
printf("export MYENV=helloworld");
return 0;
}
And you invoked it as $(./myprogram), the result of echo $MYENV would be helloworld. Tested on Bash with Arch Linux. I know this is not exactly what you want, and may not work for your purposes, but it is the best I know.

Related

How to export an environment variable for children processes inside a ksh script?

I'm working on an old C software. There is one ksh script which executes a C program, which then creates some other processes and ends. These processes remain alive.
I'm trying to set an environment variable inside my ksh script, so that it could be accessible in the newly created processes that are still alive.
I have tried this way :
#!/bin/ksh
VARIABLE=value
export VARIABLE
my_c_program
But that doesn't work... I have tried to :
change my ksh script to bash
create a wrapper script that creates and exports the variable and then executes the original ksh script (which just executes the C program)
sourcing my ksh script (or my wrapper script when trying with 2.) instead of executing
it
But nothing from that worked.
The only thing that works for now is when I explicitly, by hand, execute the command :
export VARIABLE
In the current bash terminal.
Why? Isn't it possible to do the export inside a script instead of doing it manually?
Everything is ok actually...
The fact is that the process I thought was the child of the C program executed in my ksh script was the child of another process executed before. The C program was just sending a message via shared memory to tell the other program to execute its child.
So indeed the environment variable never went from my C program to the other's child. The only time when I had that variable set in the child is when I executed the other program (the one which is the real parent of the child) in a shell where the variable was exported.
The code above looks correct and it should work. Another way to do it is:
VARIABLE=value my_c_program
which exports the variable just for the program. Afterwards, the variable will be set but other external processes don't get a copy.
So why doesn't your script work? It's hard to tell but here are some tips to debug the issue:
Use #!/bin/ksh -x to enable debug output. Save the output in a file and then grep VARIABLE to make see what happens with it.
Check for typos.
Another shell script is like an external process. So create a script
#!/bin/ksh
echo $VARIABLE
and call it instead of my_c_program just to make sure passing the variable on works.
Maybe the C does something unexpected. Use a debugger to make sure it does what you expect.

Get System Environmental Variables In C

I've written an application in C that is supposed to be reading environmental variables and handling those changes appropriately. When the application starts, I've set it up to go ahead and initialize the variable (to prevent any null pointers from returning) via setenv("MYVARIABLE", "TEST", 1).
This application loops often and, during those loops, one of its jobs is to check that global variable via getenv("MYVARIABLE").
The plan then was to have either a shell script or a python script change these environmental variables. The C application is full screen, so I have no way of testing this process without another terminal entry. In my other terminal (c2) I run commands such as:
MYVARIABLE="My New Value"
or
export MYVARIABLE="My New Value"
My application doesn't seem to catch the environmental update, though. Instead it continues to insist that MYVARIABLE is "test," and not "My New Value." I'm not sure why these two environments are separate, but I need them to work together. Does anyone know what I'm doing wrong?
My system is running Archlinux for anyone who is interested
The problem you're facing right now is that you've only set your variable in the local shell session's scope. EX:
(Assuming bash) When you set a variable such as:
MYVARIABLE="My New Value"
it only effects the current shell, when you set it as:
export MYVARIABLE="My New Value"
it effects the current shell and all processes started from the current shell.
If you set it in your .bashrc file it will set it permanently for all future sessions, but you'll need to source that file for it to work in your current session.
The best solution is to fork off a process. For example if your program is called a.out you can execute:
> ./a.out &
This will allow you to continue to work in the shell session while the program is running, then you can set variables in that same session.
The only other option I've ever seen is to force your shell session to "auto" source things as they come in:
1) In your first session enter:
trap 'source ~/.bashrc' DEBUG
2) Then start your program in the same session:
./a.out
3) In a second window edit your .bashrc file to add the new env var
4) in the second window source the new version of the file:
source ~/.bashrc
Now the first window running your program has the new var set to its session. I've used that before and I know it works, but I've never tried it on an applications that was already spawned.. but I suspect it should work for you.
Your process environment is not dynamically changeable!!!
Remember this main() prototype...
int main (int argc, char *argv[], char *envp[])
{
char *path;
/*
Searches in this process envp[] only,
There is no way it can access changes happening in Shell command prompt...
User shell does not communicate with this process, if not piped.
This will always return PATH, that was set at the time of starting.
*/
while (1)
{
path = getenv("PATH");
sleep (5);
printf("PATH = %s\n", path);
free(path);
};
}
Your understanding of getenv() library function is WRONG. To achieve your expectation, You need to use any form of IPC.
setenv() works with calling process own environment. That will be passed on to it own child process through fork(), exec() syscalls. This will will not do anything in the parent process, your shell.

Assignment of variable is not getting picked up in makefile

I am using a makefile where there is a line like
VAR=$(MVAR) command1;
Now when command1 executes i want VAR to retain the value what is being assigned by MVAR which is required for the successful completion of command1.
But unfortunately VAR contains some default value while executing command1, ie VAR is not able to hold the value assigned during assignment while executing command1.
if same command i run in any of the shell the value of VAR is retained and command1 gets executed successfully.
Nearly tried everything to run it on makefile.
Can somebody please explain me the reason behind this behavior in gnu makefile and possible solution. I am using Linux as OS and ksh as shell for Makefile.
If you are talking about a command in a rule, just add (or move) a semicolon:
VAR=$(MVAR); command1
But I suspect there may be more involved. If this doesn't work, please give us more context (e.g. the makefile and some information about command1).
what sort of thing are you expecting MYVAR to be set to? If it contains spaces or other characters meaningful to the shell, what you are doing won't work. You could try quoting it:
VAR='$(MVAR)' command1
and see if that works better.
Also, this depends on your shell being a standard POSIX bourne-shell derivative (sh, bash, ksh, zsh all qualify; the Windows cmd processor does not).
Have you tried VAR:=$(MYVAR)? (i.e. := instead of =)
This forces VAR to be assigned the value immediately, rather than being lazily evaluated.

Get environment variables using C code

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?
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...
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().
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.
You can either set the environment variable in your own process using setenv() (which system() then silently passes on to child processes, or explicitly pass the variables by using fork() and execve() to run the shell script.

Execute SET command in c program

I created a little mini shell and it let's the user enter a command like 'ls' and it will list the contents of the directory like it's supposed to using execv() in my code, but that doesn't seem to work for when the user enters something like 'set name="bob"'. I've been looking all over the place for what I should use in my code to execute a set command when the user enters it and the best I can find is system(), but that still isn't working for me. Any ideas?
set is a shell-builtin command, not an external command (indeed it needs to be to have the intended effect, which is to modify a shell variable within the shell process itself).
This means that you need to look for and handle set within your shell itself, by adding the named variable to some internal data structure that tracks shell variables (or updating it if it already exists there).
Since you're doing a fork-and-exec or a system(), the command is really being run in a separate process. What happens in that process (like setting an environment variable) does not affect the parent's environment. (A separate issue is that set doesn't actually create an environment variable. You'd need export in [ba]sh or setenv in [t]csh to do that.)
So you need to code your mini-shell to handle the set command explicitly, rather than passing it off to another program.
You might want to look at setenv(3) and getenv(3). These are functions for changing and reading environment variables from within a C program.

Resources