Environment Variables and Process ABI for Developers
What is it?
This roadmap is the developer contract for a process's environment and command line: how variables are stored and read, how %VAR% and %1 to %9 expand, how PATH finds a command, how a command line becomes argv, and which shell commands show and change variables. The kernel side has largely shipped under the kernel's Environment Variables roadmap. What this file still adds is path-template expansion, a shell command cache, a .profile start-up script and the set, env and where commands.
How does it work?
Today. Each task owns a private environment in the kernel:
- Storage.
env.hprovidesenv_get_copy(),env_set(),env_unset(),env_copy()andenv_free(). There is deliberately no publicenv_get()returning a pointer, because a pointer into another task's block could change under the caller; callers get a copy. - Defaults and expansion.
env_init_defaults()seeds a new task from the Registry, andenv_expand()expands%VAR%in a single pass: a value that itself contains%OTHER%stays literal. Output is truncated at the caller's buffer size, and work beyondENV_EXPAND_WORK_MAXis refused. - Native calls. User mode reads and writes variables through
NtQueryEnvironmentVariable(0x03DD) andNtSetEnvironmentVariable(0x03DE), listed inservice_numbers.hand registered bynt_env.c. There are noSYS_GETENVorSYS_SETENVnumbers. - Search path.
env_search_path()and theSearchPathW/SearchPathAentry points inenv_searchpath.hresolve a file name against a search path.PATHEXTis seeded as.EXEonly, and iterating it belongs to the shell's command lookup, which is not built. - Command lines. The kernel has
CommandLineToArgvW()with the Windows quoting rules. In user mode,cmd_tokenize()instdlib.csplits a line in place using the same rules, and the shell,cmd.c, uses it for every command.
Planned design. On top of that: env_expand_path() for %1 to %9 file-association templates, an eight-entry command lookup cache in the shell, a .profile script run at shell start, and the set, env and where commands.
flowchart LR
R[Registry defaults] --> E[task environment]
E -->|NtQueryEnvironmentVariable| U[user program]
L[command line] -->|cmd_tokenize| A[argv]
E -->|PATH| S[env_search_path]
What are its interfaces?
| Interface | Status |
|---|---|
env_get_copy(), env_set(), env_unset(), env_copy(), env_free() |
Shipped (kernel) |
env_expand(), env_init_defaults() |
Shipped (kernel) |
NtQueryEnvironmentVariable, NtSetEnvironmentVariable |
Shipped |
SearchPathW(), SearchPathA(), CommandLineToArgvW() |
Shipped (kernel) |
cmd_tokenize(char *line, char **argv, int max_argc) |
Shipped (user libc); the roadmap's const char * signature differs |
env_expand_path() |
Planned in section 3 |
Shell command cache, .profile, set, env, where |
Planned in sections 5, 7 and 8 |
How do I use it?
Run the environment tests in the kernel runner:
bash scripts/test.sh SUITE=abi
In the shell, echo works today; it does not expand %VAR% yet, and set, env and where are not built-in commands.
Who owns what?
02-kernel-core/TODO-22 owns and shipped storage, defaults, expansion, the native calls, the search path and CommandLineToArgvW(). This file restates much of that with names the kernel did not adopt (env_get(), SYS_GETENV, src/desktop/terminal.c). A rescope item in section 1 keeps only the parts no other roadmap covers. The initial user stack that carries argc, argv and envp belongs to the PEB, TEB and the User-Mode ABI roadmap.
What is not implemented yet?
- Per-Process Environ Storage (SDK Contract): shipped in the kernel under different names
- System Default Variables: shipped as
env_init_defaults() %VAR%Expansion andenv_expand_path:env_expand_path()does not exist- SYS_GETENV, SYS_SETENV, SYS_UNSETENV: shipped as
Ntcalls - PATH-Based Command Lookup and Session Cache: the cache and shell lookup are missing
- argv and argc Process ABI and
cmd_tokenize - Shell
.profileStartup Script set,echo,envandwhereShell Commands
How does it compare with Windows 11 and Linux?
Windows keeps the environment as a UTF-16 block behind PEB->ProcessParameters, seeds it from two Registry hives, expands %VAR% with ExpandEnvironmentStrings() and splits command lines with CommandLineToArgvW(). Linux passes envp[] to execve(), seeds from /etc/environment and ~/.profile, and leaves expansion to the shell. Impossible OS follows the Windows model, including the dual-hive defaults and the quoting rules, and adds a copy-only kernel API so no caller can hold a pointer into a block that may change.