System Information in C — getenv(), system(), and uname()

How do you get system information from a C program? There are three portable-ish layers, and knowing which to use is the actual lesson: getenv() reads environment variables (pure standard C), system() runs a shell command like uname (standard C, but it launches a whole shell), and POSIX uname() fills a struct with OS name, release, and architecture directly — no shell involved. This page shows all three with tested code and real output, plus the buffering gotcha that makes system() output appear in the wrong order.

How It Works — Three Layers

Method Header Portability What you get
getenv("HOME") <stdlib.h> Standard C environment variables: HOME, PATH, USER…
system("uname -srm") <stdlib.h> Standard C (command itself varies by OS) anything a shell command can print
uname(&info) <sys/utsname.h> POSIX — Linux, macOS, WSL OS name, release, machine, hostname — as strings in a struct

C Program: getenv() and system()

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

int main(void)
{
    const char *home = getenv("HOME");
    const char *path = getenv("PATH");
    int status;

    printf("--- From environment variables (getenv) ---\n");
    printf("HOME = %s\n", home != NULL ? home : "(not set)");
    printf("PATH is %s\n", path != NULL ? "set" : "(not set)");

    printf("\n--- From the OS (system) ---\n");
    fflush(stdout);                   /* flush before the child writes, or
                                         its output can appear out of order */
    status = system("uname -srm");    /* kernel name, release, machine */
    if (status == -1) {
        printf("Could not run the command.\n");
        return 1;
    }
    return 0;
}

C Program: POSIX uname() — No Shell Needed

#include <stdio.h>
#include <sys/utsname.h>          /* POSIX - Linux, macOS, WSL; not plain Windows */

int main(void)
{
    struct utsname info;

    if (uname(&info) != 0) {
        printf("uname() failed.\n");
        return 1;
    }
    printf("System:   %s\n", info.sysname);
    printf("Release:  %s\n", info.release);
    printf("Machine:  %s\n", info.machine);
    printf("Nodename: %s\n", info.nodename);
    return 0;
}

How to Compile and Run

gcc -ansi -Wall -Wextra -o sysinfo sysinfo.c
./sysinfo

gcc -ansi -Wall -Wextra -o uname_demo uname_demo.c
./uname_demo

Sample Output

Program 1 (on an Apple Silicon Mac):

--- From environment variables (getenv) ---
HOME = /Users/sn
PATH is set

--- From the OS (system) ---
Darwin 25.5.0 arm64

Program 2, same machine:

System:   Darwin
Release:  25.5.0
Machine:  arm64
Nodename: Sandeepas-Laptop.local

On Linux you’d see Linux 6.8.0-... x86_64; on WSL, the same with a -microsoft-standard suffix in the release string — an easy way for a program to detect it’s running under WSL.

Code Explanation

  • getenv() returns NULL when a variable isn’t set — always check before printing, or you hand %s a null pointer (undefined behavior). The returned string must not be modified.
  • fflush(stdout) before system() — the load-bearing line. Your printf output sits in a buffer; the child process writes straight to the terminal. Without the flush, the child’s output can appear before text you printed earlier. (This is the same reason exams ask about printf ordering across fork().)
  • system() returns −1 if the shell itself couldn’t launch; otherwise it returns the command’s exit status. It’s convenient but heavyweight (spawns /bin/sh) and dangerous with untrusted input — never build a system() string from user input; that’s command injection.
  • struct utsname — five fixed string fields filled in one call. No parsing, no child process, and it can’t be hijacked by a modified PATH, which is why production code prefers it to system("uname").
  • Windows note: plain Windows has neither <sys/utsname.h> nor uname; the equivalents are GetVersionEx()/GetNativeSystemInfo(). Program 2 compiles fine under WSL or MinGW’s POSIX layer.

What This Program Teaches

  • The three layers of “asking the system” — environment, shell-out, and syscall wrapper — and their trade-offs
  • Buffered stdout vs child-process output, and why fflush() fixes the ordering
  • Why system() with untrusted input is a command-injection vulnerability
  • Standard C vs POSIX: what -ansi guarantees and what the OS adds

Related C Programs

Test yourself: our free C Programming Quiz app for Android has 150+ questions with explanations for every answer.

Recommended Book

The C Programming Language by Kernighan & Ritchie covers the standard-library layer; we’ve solved all of its exercises. Also on Amazon.com.

1 comment on “System Information in C — getenv(), system(), and uname()

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>