Wild Ride from Raw Syscalls to Figuring Out NSS and libc
Source: Dev.to
I’ve always had a nerdy obsession with Linux. From day one I dove head‑first into the kernel, writing tiny tools using raw syscalls—skipping libc entirely and talking to the kernel directly. I made my own headers, wrote my own printf‑style routines, opened directories with getdents64, parsed /proc for processes… basically living dangerously in the kernel’s backyard.
At the time I thought I was the shit doing everything “the real way.” It worked, but I was skipping a whole layer of sanity that exists in libc. I had no idea about all the stuff it actually does: buffering, human‑readable formatting, and other niceties that make life less painful.
The WTF Moment: /etc/nsswitch.conf
Everything hit me when I stumbled upon /etc/nsswitch.conf while poking around network and user stuff. I opened it and literally went:
“What the actual hell is this? Who the fuck uses this? Isn’t the kernel supposed to know this?”
I was in full raw‑syscall mode, so the idea that some config file could tell libc how to handle getpwnam() or getaddrinfo() blew my mind. I had no clue why this “sign‑board” was even necessary.
Okay, Now NSS Makes Sense
Turns out, NSS = Name Service Switch. It’s basically a sign‑board inside libc telling it where the hell to look for information.
When a program calls something like getpwuid() or getpwnam(), it’s asking:
“Who is this user? What’s their username?”
The kernel doesn’t know usernames; it only knows numeric IDs (e.g., UID = 1000). That’s where libc + NSS swoop in like superheroes.
How It Actually Works
Say you call getpwnam("Deadpool"). libc does roughly the following:
-
Read
/etc/nsswitch.conf
Finds the line for users:passwd: files systemdThis means:
- First, check local files (
/etc/passwd) - Then, ask systemd’s dynamic user database
- First, check local files (
-
Call each NSS module in order
-
libnss_files.so.2→ looks in/etc/passwd- Found? → return result
- Not found? → continue
-
libnss_systemd.so.2→ asks systemd- Found? → return result
- Not found? → continue (if there are more sources)
-
-
Return a
struct passwdto your program, containing username, UID, GID, home directory, shell, etc.
Important: NSS itself is not a running program; it’s just a config file plus a set of shared libraries.
libcloads them dynamically, so no extra processes are involved.
The Secret Sauce: libc Beyond Raw Syscalls
libc is more than a thin wrapper around syscalls. Sure, it wraps things like write, read, open, getpid, but it also adds a ton of extra functionality:
| Category | Examples |
|---|---|
| Buffering & formatting | printf, fprintf, fopen, getline |
| Memory management | malloc, free, realloc |
| Threading support | pthread_* functions |
| Policy & lookup logic | NSS modules, locale handling, hostname resolution |
Without NSS, this extra sauce works partially:
- Syscalls still happen correctly (
open,read,write, …) - Buffering, formatting, memory, threading still work
- Any function that needs human‑readable names (users, groups, hosts, services) would fail or return
NULL, becauselibchas no map telling it where to look.
NSS Modules Still Use Syscalls Under the Hood
Even NSS modules ultimately rely on syscalls:
libnss_files.so.2reads/etc/passwdusingopen,read,close.libnss_systemd.so.2talks to the systemd service via IPC syscalls.
So everything still hits the kernel; NSS just adds a layer of logic so libc knows where and how to get the data.
Bottom line:
libc+ NSS = fancy middleware. Raw syscalls = truth. NSS gives meaning to that truth and ensures humans don’t see useless numbers.
How This Hit Me
Before the epiphany, my tools did things like:
- Open directories with
getdents64 - Read
/proc/<pid>/status→ get numeric UIDs - Display numbers only
That’s fine if you just want raw truth, but it’s boring for humans. Humans want:
PID: 742 User: Deadpool Process: bash
instead of:
PID: 742 UID: 1000 Process: bash
NSS basically translates truth into meaning, and libc does all the heavy lifting.
Layers of the System (Mental Picture)

Key nuggets I learned
libcdoes way more than just wrap syscalls: buffering, formatting, threading, memory, and NSS.- NSS is optional if you only need numbers, but humans will hate your output.
- Systemd has a dynamic user database for services and containers, which NSS can query.
- Raw syscalls = truth; NSS = meaning.
My Reflection
Real talk: this was a big “oh‑shit” moment. My low‑level tools were cool, but they weren’t really “user‑friendly” or system‑aware. I’d been skipping an entire ecosystem of userspace logic.
Now I see the value in combining both:
- Raw syscalls → understand the kernel and ABIs like a boss.
libc+ NSS → make tools that are readable, robust, and actually useful.
One‑Liner to Summarize
“Syscalls give you the raw truth; NSS gives
libcthe damn map.”
to turn that truth into something humans can actually understand.