glibc 2.29 lets spawned programs choose their directory
The new spawn directory actions move another piece of child setup into glibc, alongside changes to scanf compatibility and runtime interfaces.
Changing a child's working directory sounds like a small requirement until it determines which process creation interface an application can use. glibc 2.29's release announcement introduces directory actions for posix_spawn and posix_spawnp. A launcher can now describe that part of child setup using the library's action list. The addition makes an existing process creation interface useful for programs whose files must resolve relative to a particular directory.
I find these changes interesting because C's practical importance extends well beyond its language grammar. Its library interfaces describe the operations through which many other languages reach the operating system. A more complete spawn interface can therefore matter to a runtime author as much as to someone writing a C command launcher.
Recording setup before creating the child
The new functions are posix_spawn_file_actions_addchdir_np and posix_spawn_file_actions_addfchdir_np. Both are GNU extensions according to the release announcement, which also notes that the pathname form resembles the Solaris function of the same name. The 2.29 spawn header places their declarations behind its GNU extension condition. An application requesting these declarations defines _GNU_SOURCE before including the headers.
The two calls record actions. Adding a pathname action does not immediately change the caller's directory. The implementation of the pathname action duplicates the supplied string, makes room in the action array if necessary, and appends a record tagged as a directory change. Allocation failure returns ENOMEM. This separation matters because building a description of child setup and performing that setup have different failure points.
A small launcher can use that description without calling a shell. This function returns the spawn operation's error number and supplies a child identifier on success:
#define _GNU_SOURCE
#include <spawn.h>
#include <sys/types.h>
int launch_in(pid_t *child, const char *directory,
char *const arguments[], char *const environment[])
{
posix_spawn_file_actions_t actions;
int error = posix_spawn_file_actions_init(&actions);
if (error != 0)
return error;
error = posix_spawn_file_actions_addchdir_np(&actions, directory);
if (error == 0)
error = posix_spawn(child, "/bin/pwd", &actions, 0,
arguments, environment);
posix_spawn_file_actions_destroy(&actions);
return error;
}
The argument array must describe the executable being launched and end with a null pointer, as must the environment array. The example uses an absolute executable pathname to keep the program selection separate from the directory used by the child. It leaves waiting for the child to its caller. Successful creation does not report the eventual exit status of the executed program.
Ordering is part of the interface
The header explicitly says a directory action affects subsequent file actions. That makes the action list an ordered description of setup. A relative pathname in an open action added after a directory change resolves under the new directory. Placing the open first gives it the earlier directory context. These sequences express different requests even when they contain the same individual actions.
The descriptor form has a separate lifetime requirement. The header states that the file descriptor is not duplicated and must remain open when the action executes. Recording a descriptor number therefore does not retain an independent reference for the application. Closing or repurposing it before spawning invalidates the application's assumption about which directory it names.
The Linux spawn implementation in the release makes the ordering visible. It walks the action array in insertion order. A pathname directory action invokes the internal chdir operation, while a descriptor directory action invokes fchdir. Either failure enters the spawn failure path before the final execution call. An action record is consequently a deferred operation, not proof that the requested directory exists or is accessible.
This is an interface design I like in C. It makes the required setup explicit and lets the library own the restricted execution phase. The caller still owns the action object's lifetime, while glibc owns the sequence that applies it to the child.
Why the runtime controls the setup phase
The Linux implementation uses clone with CLONE_VM and CLONE_VFORK, together with an allocated child stack. The source explains that shared memory creates constraints on signal handlers and on freeing the stack. It blocks signals and arranges synchronization so the calling thread waits until the child executes a program or exits.
These implementation details explain the value of adding a library action for a common requirement. Arbitrary application callbacks in that interval would have to obey constraints imposed by the runtime's process creation strategy. A specific directory action gives glibc a known operation to execute inside the sequence it controls. The public interface remains a description assembled before that interval begins.
Error reporting also spans the parent and child. The implementation provides a field through which the child records setup or execution errors. The parent examines it after the synchronized phase and releases the temporary stack. This is why a launcher must distinguish failure to construct an action list from failure to apply the completed list. Both are meaningful even before the launched program starts doing its own work.
Compatibility changes elsewhere in 2.29
The release also changes an old GNU scanf extension. Its allocation forms using %as, %aS and %a[...] conflict with C99's hexadecimal floating-point conversion. glibc now restricts the old allocation interpretation to C89 or C++98 modes with _GNU_SOURCE. The announcement directs applications needing allocated string input to the POSIX m modifier. This is a source-level compatibility change whose significance depends on the language mode used to compile the caller.
That detail illustrates why a runtime release can affect unchanged application text. A format string's meaning depends on the selected interface conventions. The release notes say GCC's format warnings can find most uses of the old extension, provided wrappers around the vscanf family carry the appropriate format attributes. The diagnostic opportunity belongs at the wrapper declaration as well as at direct calls.
Other additions include the Linux-specific getcpu wrapper and broader visibility for reallocarray, now declared under _DEFAULT_SOURCE. The announcement also lists optimized generic mathematical functions. None of these changes requires a new edition of the C language. They change the capabilities available through a particular C runtime.
For programs using GNU extensions, the spawn directory actions supply a precise new way to express child setup. Their practical contract includes the placement of relative file operations and the lifetime of a directory descriptor. Those details belong in the launcher's design because they determine what the child will actually execute and which files its preparation will open.