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()returnsNULLwhen a variable isn’t set — always check before printing, or you hand%sa null pointer (undefined behavior). The returned string must not be modified.fflush(stdout)beforesystem()— the load-bearing line. Yourprintfoutput 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 aboutprintfordering acrossfork().)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 asystem()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 tosystem("uname").- Windows note: plain Windows has neither
<sys/utsname.h>noruname; the equivalents areGetVersionEx()/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
-ansiguarantees and what the OS adds
Related C Programs
- Set Up C Programming on macOS — where the Darwin/arm64 output above comes from
- How to Compile C on Linux
- Install GCC on Windows 11 (WSL2 and MSYS2) — where the WSL detection trick matters
- sleep() Function in C — another standard-C-vs-POSIX boundary
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()”
where utsname.h is defined?whether it is predefined or we have to write that?