Accessing user defined environment variables in C program in linux? - c

I am new in linux.I have problem in accessing my defined environment varibales in C program.
I have defined one variable in linux command terminal as follows:
$ ExampleVar="Hi"
And in C program I am trying to access it using
getenv("ExampleVar")
But it is null every time. When I try to access other environment variables like USER, getenv gives correct results. I have also tried extern collection of unistd.h
It is not showing ExampleVar too.
Please help me.

It depends upon your shell. If you use bash -see bash(1) for more- type
export EXAMPLEVAR="Hi"
in the shell (e.g. in the terminal before running your program), then use getenv("EXAMPLEVAR") in your C program. See getenv(3)
If you don't export a bash variable foo, you still can use $foo in your bash commands, but getenv("foo") would fail and return NULL from inside compiled C programs.
Conventionally, environment variables have full capital names. See environ(7)
See also env(1) command.

Related

C program getenv() returns null

After I call export EXAMPLE="exampleValue" in terminal. I call command printenv I can clearly see my new environment variable.
I compile C program and run it and in code I call getenv("EXAMPLE").
But it returns null every time, unless I call
getenv("PATH") or getenv("HOME") or similar environment variables.
char* env_variable_name;
env_variable_name = getenv("EXAMPLE");
Any explanation why I can't get value of EXAMPLE?

Using linux system() function C++ beagle bone issue [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 5 years ago.
Improve this question
I have searched around but can't find an answer to my problem.
I am cross compiling a C++ application for the beagle bone black and wish to use the linux system() function as follows :
system("echo DM-GPIO-Test > $SLOTS");
It is to add a device overlay to control GPIO pins. The echo command "echo DM-GPIO-Test > $SLOTS" works fine when executed directly on the terminal on the beagle bone from anywhere. SLOTS is an environmental variable I defined and DM-GPIO-Test-00A0.dtb0 is in /lib/firmware
I get the following error on execution of the c++ application however:
"sh: 1: cannot create : Directory nonexistent"
Is it incorrect to call the system function like I did ?
Thanks in advance
system("echo DM-GPIO-Test > $SLOTS");
This smells bad and should be avoided.
What you probably want is to write a string in a file given by your SLOTS environment variable (see environ(7)). For that particular use, you don't need to fork any /bin/sh process (which is what system(3) does). You could simply fetch that environment variable using getenv(3).
So you might try:
const char*slotspath = getenv("SLOTS");
if (!slotspath) {
fprintf(stderr, "no SLOTS\n");
exit(EXIT_FAILURE);
}
FILE* fslots = fopen(slotspath, "w");
if (!fslots) { perror(slotspath); exit(EXIT_FAILURE); };
fputs("DM-GPIO-Test\n", fslots);
fclose(fslots), fslots = NULL;
Be aware that the environment of your program -assuming it is started by some other utility (or from init or systemd)- is probably different (and smaller) than your interactive environment.
Perhaps your slotspath should not come from your environment, but from some configuration file under /etc/ (that your program should parse), or some program argument.
So I suggest to define the format of some configuration file and parse it, and get your slotspath from it.
No, it doesn't display anything doing system("echo $SLOTS")
In this case the environment variable SLOTS is simply unset/empty in the environment used by your application. Depending on your use case you either need to set it before you start the binary, or using setenv(), or replacing it directly in the string you pass to system(). If you expect the variable to be set in any of the user's profile settings you need to be aware of the different behaviour of your shell (e. g. bash) at invocation, and place it in the right file, or create a wrapper script that sets it.
$ cat .profile
# ~/.profile: executed by the command interpreter for login shells.
# This file is not read by bash(1), if ~/.bash_profile or ~/.bash_login
# exists.
[...]
A good idea would also be to check it for valid values in your application prior to the system() call, using getenv() to obtain it and asserting if it doesn't match the requirements.
Be aware that it is best practice to enclose shell variables containing strings into double quotes, in case they contain spaces, and the identifier into curly brackets, to avoid ambiguities:
system("echo DM-GPIO-Test > \"${SLOTS}\"");
And as Basile points out in a separate answer, avoiding invocation of the shell and handling the logic of writing the file completely in C would even be more powerful regarding the handling of errors and special cases, but also results in more code (which could contain bugs of its own...).

compile program with set runtime environment?

I am trying to compile a program which, when run, will be run in a predefined environment in c.
More specifically, I am looking to somehow include LD_PRELOAD=./lib.so so that when I run ./program it will run as if I had used the command LD_PRELOAD=./lib.so ./program
Does anyone know if this is possible?
Make the program set LS_PRELOAD and then run itself if LS_PRELOAD isn't set. Use execv() for that.

Apache APR function apr_procattr_cmdtype_set confusion

I've been following tutorials online on C coding, and the code is using the Apache APR library.
It uses the apr_proc_t structure to execute an external app.
I'm confused about this function, could someone explain what this function means:
apr_status_t apr_procattr_cmdtype_set ( apr_procattr_t * attr,
apr_cmdtype_e cmd
)
Set what type of command the child process will call.
Parameters:
attr The procattr we care about.
cmd The type of command. One of:
APR_SHELLCMD -- Anything that the shell can handle
APR_PROGRAM -- Executable program (default)
APR_PROGRAM_ENV -- Executable program, copy environment
APR_PROGRAM_PATH -- Executable program on PATH, copy env
The apr_procattr_cmdtype_set function is used to tell APR how you want to execute the external command, it probably just sets an internal flag and does a bit of bookkeeping.
Let us look at the enum apr_cmdtype_e:
APR_SHELLCMD
use the shell to invoke the program
APR_PROGRAM
invoke the program directly, no copied env
APR_PROGRAM_ENV
invoke the program, replicating our environment
APR_PROGRAM_PATH
find program on PATH, use our environment
APR_SHELLCMD_ENV
use the shell to invoke the program, replicating our environment
The first and last options (APR_SHELLCMD and APR_SHELLCMD_ENV) pretty much say "use a portable version of system" (with or without copying the current environment variables to the new process). The others are just variations on the Unix fork/exec pair with the flag choosing which of the exec family of functions to use.

understanding requirements for execve and setting environment vars

We are having a lot of trouble interpreting our teacher. We asked for clarification and got the following back from him
For execve, send it a environment you setup with your exported variables and create a builtin command to spawn a subshell of /bin/bash, that way you can see your exported variables using env.
(He is talking about creating our own environment vars here.)
Yes create your own. You can start by copying environ when your shell starts and add only exported variables
This is related to the following post on Stack Overflow by me (reading this other post will help you understand what I am trying to do):
using a new path with execve to run ls command
We are just very confused about this. One more time I will explain what we are trying to do now. Similar to how your Linux shell does this, we need to write our own program that can set environment variables like PATH and USER and whatever other vars the user wants to define.
An example of how you would call this would be (inside your program at its prompt):
mysetenv dog spike
which would create an environment variable looking like "dog=spike"
More importantly, we need to be able to set our own PATH variable and send it to an exec command. This is the confusing part because, based on all of our questions, we don't understand what we are supposed to do.
It is actually very simple. You already know that your arguments are a list of char *, terminated by a NULL pointer. Similarly, the environment is simply a list of char *, terminated by a NULL pointer. Conventionally, the values in the list take the form VARNAME=var-value, though you can pass other formats if you wish.
So, to take a simple case:
#include <unistd.h>
#include <stdio.h>
int main(void)
{
char *argv[] = { "/bin/sh", "-c", "env", 0 };
char *envp[] =
{
"HOME=/",
"PATH=/bin:/usr/bin",
"TZ=UTC0",
"USER=beelzebub",
"LOGNAME=tarzan",
0
};
execve(argv[0], &argv[0], envp);
fprintf(stderr, "Oops!\n");
return -1;
}
In this example, the program will run /bin/sh with arguments -c and env, which means that the shell will run the env program found on its current PATH. The environment here is set to contain 5 values in the orthodox format. If you change env to date (or env; date), you will see the effect of the TZ setting, for example. When I run that on my MacOS X machine, the output is:
USER=beelzebub
PATH=/bin:/usr/bin
PWD=/Users/jleffler/tmp/soq
TZ=UTC0
SHLVL=1
HOME=/
LOGNAME=tarzan
_=/usr/bin/env
The shell has added environment variables SHLVL, _ and PWD to the ones I set explicitly in the execve() call.
You can also do fancier things, such as copy in some of the other environment variables from your genuine environment where they do not conflict with the ones you want to set explicitly. You can also play games like having two values for a single variable in the environment - which one takes effect? And you can play games with variable names that contain spaces (the shell doesn't like that much), or entries that do not match the 'varname=value' notation at all (no equals sign).
I'm a little late to the party here, but if you want to preserve the old environment variables as well as creating your own, use setenv, and then pass environ to execve().
setenv("dog", "spike", 1);
extern char** environ;
execve(argv[0], argv, environ);
environ is a variable declared in unistd.h, and it keeps track of the environment variables during this running process.
setenv() and putenv() modify environ, so when you pass it over execve(), the environment variables will be just as you'd expect.
The code from Jonathan Leffler works great, except if you want to change the PWD (working directory) variable.
What I did, in order to change the working directory, was to put a chdir(..) before execve(..) and call:
chdir("/foo/bar");
execve(argv[0], &argv[0], envp);

Resources