C++ getenv always returns null value - c++

I have just added the environment variable "DataDir", but the getenv function still returns null value.
Here is my code:
const char *ret = getenv("DataDir");
I restarted my computer and it done.

did you remember to export the variable before running the program? If you are using bash shell on linux, for example, you generally should use export DataDir="..."
On windows, if you set the environment variables using the system settings window, it will not immediately propagate to all of the running programs. If "I restarted my computer and it done." means "restarting the computer resolved the issue", then I believe that explains the problem. After changing the environment variable, try closing all programs and then start a CMD session (or visual studio) and run the program again

Are you running on Windows? Did you set the environment variable through the control panel? If so, that only affects processes that you start (programs that you launch) after you changed the setting. If you're running from a command prompt, and the command prompt didn't inherit the new environment variable, then your program won't inherit it either.
After rebooting, all new processes inherit the new environment variable.
On the other hand, if you set the variable and then run the program:
C:\>set DataDir=blah
C:\>.\my_program
then your program will inherit the variable (but it won't persist across a reboot).
Similar considerations apply on Linux and other systems, but the details differ.
Note that I'm only guessing, based on the symptoms you reported, what system you're using. In the future, it would be helpful to provide that information in the question (if it's not relevant we can ignore it).

Related

Starting the default terminal on Linux

Tell me, please, is it possible to call the Linux Terminal, which is installed by default, in some way (method)?
Now, I run the process in the xfce4-terminal terminal, specifying this terminal and the arguments to it:
QProcess up;
QString cArg;
cArg="/tmp/cp.py -y " + ye;
up.start("xfce4-terminal", QStringList()<< "--geometry=120x40" << "--command" << "python3 "+ cArg << "-H");
up.waitForFinished();
up.close();
No, there is no general way in the Linux kernel to find out which (or whether a) terminal emulator is installed on the system by default.
Although the (fairly ubiquitous) freedesktop.org specifications describe how to associate MIME type with a default application, there isn't a specification for default application without an associated file to be opened as far as I can tell. Each desktop environment that has a concept of "default terminal emulator" has their own way of configuring it.
Debian has has "update-alternatives" system that allows configuration of "default" applications based on aliases, and it has a package that creates an alias x-terminal-emulator that can be used to configure the default terminal emulator.
Here is a reasonable strategy for choosing the command in your program:
Let the user configure the command. Use this with highest priority if configured.
Use XDG_CURRENT_DESKTOP environment variable, and implement logic for each desktop environment to read their configuration to find out the configured default emulator. Use this as the second highest priority when available.
Collect a list of commonly used terminal emulators. Put aliases such as x-terminal-emulator with higher priority in the list.
With this list starting with user configuration and ending with your hard-coded list, check each command and see if it's executable and pick the first one that is. In case user has configured the command, and it isn't executable, I recommend an optional error message.
You can use i3's sensible-terminal script.
https://github.com/i3/i3/blob/next/i3-sensible-terminal
While it has been made for i3, if you read the source you'll see it's very simple and doesn't rely on it, so feel free to use this within whatever desktop environment you want.
Though if you don't use i3 you may want to remove the last line (which isn't that important as you are unlikely to have no terminal installed at all).
Explanations
It proceeds by getting, in this order:
A terminal you may have defined in the non-standard $TERMINAL environment variable
x-terminal-emulator which is a similar utility for Debian only
A list of hardcoded terminals, namely mate-terminal gnome-terminal terminator xfce4-terminal urxvt rxvt termit Eterm aterm uxterm xterm roxterm termite lxterminal terminology st qterminal lilyterm tilix terminix konsole kitty guake tilda alacritty hyper

Set current user environment variable from c++ code visible to other process like cmds

I need to set a local environment variable for current user and it shoukd be visible to other processes like a new command prompt. I need it for windows. I have tried options like putenv and editing the registry from C++ code but the new cmd prompt see the old values. Primarily i need to edit PATH variable along with few custom env variables. Will appreciate if i can get a working sample code.
Please note that the environment variable need to persist past program execution.
My requirement is for windows. I even tried running setx from C++ code and it works fine but for PATH variable it trims it down to 1024 character and i lose the update. Is there a workaround to this?
IF my wording looks confusing about the requirement. I need exactly same behavior as if i am using setx.
Thanks in advance.
If you start Cmd.exe from your process you can control its environment. The environment variables are inherited from the parent process. They can also be overridden when you call CreateProcess.
If you change the users/system environment configuration in the registry(HKCU\Environment/HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment) and log off/reboot then the first process will use these new defaults.
If you update the registry you can tell other applications to refresh their environments without logging off by broadcasting a message:
BroadcastSystemMessage(0, 0, WM_SETTINGCHANGE, 0, (LPARAM)TEXT("Environment"));
In reality it is only Explorer.exe that reacts to this message but that is enough to affect new applications started from the taskbar/start menu.
The setx command is actually an executable that sets values in the registry. If you are looking to simulate the behavior where you can set an environment variable that will last longer than the current process you will need to write it to the HKCU\Environment key. The HKCU is for the the current user and can be written to without elevated permissions.
Use RegEdit.exe or reg.exe query HKCU\Environment to view the current settings. From C/C++ you can use the Registry functions. If you can, I recommend using the ATL CRegKey class as it follows RAII and ensures handles are properly cleaned up.

How to return a command from a c++ application to parent terminal?

Is it possible to run a c++ application from a terminal and on certain conditions return a command back into the terminal from which it was called from? For instance, if I were to run an application within my terminal and after my selections; my application needs to change my PATH by running an export command such as:
(USING BASH)
export PATH=.:/home/User/application/bin:$PATH
After I'm done and before my application completely closes can I make the application change my terminals local environment variables with the above command? Does Qt offer a way of doing this? Thanks in advance for any help!
No, you cannot change parent application environment.
Why? When your parent app started yours (probably using system()), it actually fork()ed - child process was born as to be almost exact replica of parent, and then that child used execve() call, which completely replaced executable image of that process with executable image of your application (for scripts it would be image of interpreter like bash).
In addition to that, that process also prepared few more things. One is list of open files, starting with file handles 0,1,2 (stdin, stdout, stderr). Also, it created memory block (which belongs to child process address space) which contains environment variables (as key=value pairs).
Because environment block belongs to your process, you can change your own environment as you please. But, it is impossible for your process to change environment memory block of parent (or any other process for that matter). The only way to possibly achieve this would be to use IPC (inter-process communication) and gently ask parent to do this task inside of it, but parent must be actively listening (on local or network socket) and be willing to fulfill such request from somebody, and child is not any special compared to any other process in that regard.
This also reason why you can change environment in bash using some shell script, but ONLY using source or . bash macro - because it is processed by bash itself, without starting any external process.
However, you cannot change environment by executing any other program or script for reasons stated above.
The common solution is to have your application print the result to standard output, then have the invoker pass it to its environment. A textbook example is ssh-agent which prints an environment variable assigment; you usually invoke it with eval $(ssh-agent)

In Linux C++, how do you read an environment variable of a specified user?

I know that getenv() returns a value of specified environment variable of the current user, but my code requires root privileges so getenv() would only use the sudo environment variables. I also know that SUDO_USER tells which user is invoking sudo, which is the user environment I want use for getenv().
char* gnome_env_var = getenv("GDMSESSION"); //returns null as not found in sudo env
char* usr = getenv("SUDO_USER");
Is there a way I can get the value of an environment variable for the logged in user, not the sudo environment?
EDIT
Okay, so what I'm hearing is that the set of environment variables are unique to each process, not user and using sudo to invoke a process with root privileges calls execve which can create an entirely new set of environment variables for that process. So to rephrase, is there a way besides messing with the sudoers file, and within the current process, of finding the calling process's environment variables?
I particularly need the GDMSession environment variable.
getenv doesn't tell you about the environment variables of the current user, but the current process. Users are free to have as many environments as they want(and can create processes), for example with the export shell built-in. In every call to execve, the calling program is free to create an entirely new environment for the executed process.
Therefore, there is no way to get the environment variables of the user, or even those of the process executing sudo. Why do you want that anyways?
You can, however, configure sudo to keep some or all environment variables, via the keep_env and reset_env directives in /etc/sudoers.
There isn't a "user environment." Every process has its own copy of the environment variables. They don't even automatically inherit -- that they appear to is an illusion maintained by the shell and the C library. It is more accurate to think of them as a second set of command line arguments to every program.
So before we can answer your question, you need to clear up what you mean! There are possibilities - none of them are elegant, mind, but they do exist - but they depend crucially on which environment variable you want to get at in which process's state and why.

How can I change Windows shell (cmd.exe) environment variables from C++?

I would like to write a program that sets an environment variable in an instance of the shell (cmd.exe) it was called from. The idea is that I could store some state in this variable and then use it again on a subsequent call.
I know there are commands like SetEnvironmentVariable, but my understanding is that those only change the variable for the current process and won't modify the calling shell's variables.
Specifically what I would like to be able to do is create a command that can bounce between two directories. Pushd/Popd can go to a directory and back, but don't have a way of returning a 2nd time to the originally pushed directory.
MSDN states the following:
Calling SetEnvironmentVariable has no
effect on the system environment
variables. To programmatically add or
modify system environment variables,
add them to the
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Session
Manager\Environment registry key, then
broadcast a WM_SETTINGCHANGE message
with lParam set to the string
"Environment". This allows
applications, such as the shell, to
pick up your updates. Note that the
values of the environment variables
listed in this key are limited to 1024
characters.
Considering that there are two levels of environment - System and Process - changing those in the shell would constitute changing the environment of another process. I don't believe that this is possible.
A common techniques is the write an env file, that is then "call"ed from the script.
del env.var
foo.exe ## writes to env.var
call env.var
In Windows when one process creates another, it can simply let the child inherit the current environment strings, or it can give the new child process a modified, or even completely new environment.
See the full info for the CreateProccess() win32 API
There is no supported way for a child process to reach back to the parent process and change the parent's environment.
That being said, with CMD scripts and PowerShell, the parent command shell can take output from the child process and update its own environment. This is a common technique.
personly, I don't like any kind of complex CMD scripts - they are a bitch to write an debug. You may want to do this in PowerShell - there is a learning curve to be sure, but it is much richer.
There is a way...
Just inject your code into parent process and call SetEnvironmentVariableA inside
cmd's process memory. After injecting just free the allocated memory.