Skip to Content
Course content

93: Environment Variables: getenv, setenv

Click on the "Edit" button in the top corner of the screen to edit your slide content.

A few years ago, I was reviewing a pull request for a tool that handled database migrations. The developer had hardcoded the database password and the server IP address directly into a header file. My first reaction wasn't just "this is a security nightmare," but "this is a maintenance disaster." Every time they wanted to move the tool from a local test environment to a staging server, they had to change the code and recompile the entire binary. I sat them down and showed them how to pull those values from the environment instead. It changed their workflow from "edit-compile-deploy" to simply changing a line in a shell script.

Reading the Shell's State with getenv

In C, the environment is essentially a collection of strings—key-value pairs—passed to your program by the operating system. To grab one of these, we use getenv() from <stdlib.h>. It takes the name of the variable you're looking for and returns a pointer to the value string.

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

int main() {
    char *api_key = getenv("APP_API_KEY");

    if (api_key == NULL) {
        printf("Error: APP_API_KEY is not set. The app cannot start.\n");
        return 1;
    }

    printf("API Key loaded successfully: %s\n", api_key);
    return 0;
}

One thing you need to be careful about: getenv() returns a pointer to the actual environment string. Do not attempt to free() this pointer, and be wary about modifying the string directly. If you need to manipulate the value, copy it into your own buffer using strcpy or strdup first. I've seen plenty of segmentation faults from developers trying to free() something they didn't malloc().

Manipulating Variables with setenv

While you'll spend most of your time reading environment variables, there are cases where your program needs to set them—perhaps to configure a child process you're about to launch via exec(). For this, we use setenv(). It takes three arguments: the name, the value, and an integer flag that decides whether to overwrite the variable if it already exists.

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

int main() {
    // setenv("NAME", "VALUE", overwrite)
    // 1 means overwrite, 0 means leave it alone if it exists
    if (setenv("LOG_LEVEL", "DEBUG", 1) == 0) {
        printf("Log level set to DEBUG\n");
    }

    char *current_log = getenv("LOG_LEVEL");
    printf("Current environment LOG_LEVEL: %s\n", current_log);

    return 0;
}

You might see putenv() in older codebases. Avoid it if you can. putenv() is trickier because it doesn't copy the string you give it; it actually puts the pointer you provide into the environment. If that pointer was to a local variable on the stack, your environment becomes a minefield of dangling pointers the moment the function returns. setenv() is much safer because it manages the memory for you.




📋 Practical Task

Build a Dynamic Configuration Loader

Create a program that simulates a service starting up. The program should check for two environment variables: DB_PORT and DB_HOST.

  • If both variables are present, print a message saying "Connecting to [HOST] on port [PORT]...".
  • If DB_PORT is missing, the program should use setenv() to set a default port of "5432" and notify the user that a default is being used.
  • If DB_HOST is missing, the program should print a critical error and exit with a non-zero status, as the host cannot be guessed.

Test your program by running it in the terminal with different setups (e.g., DB_HOST=localhost ./config_loader).

Rating
0 0

There are no comments for now.

to be the first to leave a comment.