Download raw body.
posix_spawn_file_actions_addchdir/addfchdir
On Thu, 06 Aug 2026 14:24:15 +0200,
Stuart Henderson <stu@spacehopper.org> wrote:
>
> posix_spawn_file_actions_addchdir and posix_spawn_file_actions_addfchdir
> have been implemented as _np in some OS for a while, but have made it
> into posix now.
>
> https://pubs.opengroup.org/onlinepubs/9799919799.2024edition/functions/posix_spawn_file_actions_addchdir.html
>
> a number of things in ports print information about testing for them
> during configure (mostly via gnulib which provides replacements).
> also the subprocess mode in llama.cpp is forcibly disabled via a
> patch because we don't have ..._addchdir().
>
> https://exopi.bsdfrog.org/cgi-bin/omega?P=posix_spawn_file_actions_addchdir&DEFAULTOP=and&DB=default&FMT=query&xDB=default&xFILTERS=5
>
> do we want them (yet)? is this diff vaguely along the right lines?
> (obviously needs shlib bump and testing in ports bulk).
>
> I wrote an initial diff, then discovered FreeBSD had them already
> (then added the missing free which I spotted after comparing ;)
>
NetBSD also has them:
https://man.netbsd.org/posix_spawn_file_actions_addchdir.3
And yes, we need them for ports, I had encountered at least one case when it
required: llama.cpp via https://github.com/sheredom/subprocess.h
Your diff reads sane enough, but I not sure that can review it the right way.
> Index: include/spawn.h
> ===================================================================
> RCS file: /cvs/src/include/spawn.h,v
> diff -u -p -r1.3 spawn.h
> --- include/spawn.h 20 May 2015 22:50:07 -0000 1.3
> +++ include/spawn.h 6 Aug 2026 12:18:31 -0000
> @@ -69,6 +69,9 @@ int posix_spawn_file_actions_addopen(pos
> int, const char *__restrict, int, mode_t);
> int posix_spawn_file_actions_adddup2(posix_spawn_file_actions_t *, int, int);
> int posix_spawn_file_actions_addclose(posix_spawn_file_actions_t *, int);
> +int posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t *__restrict,
> + const char *__restrict);
> +int posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t *, int);
>
> /*
> * Spawn attributes
> Index: lib/libc/Symbols.list
> ===================================================================
> RCS file: /cvs/src/lib/libc/Symbols.list,v
> diff -u -p -r1.95 Symbols.list
> --- lib/libc/Symbols.list 24 Oct 2025 11:30:06 -0000 1.95
> +++ lib/libc/Symbols.list 6 Aug 2026 12:18:31 -0000
> @@ -725,8 +725,10 @@ pause
> pclose
> popen
> posix_spawn
> +posix_spawn_file_actions_addchdir
> posix_spawn_file_actions_addclose
> posix_spawn_file_actions_adddup2
> +posix_spawn_file_actions_addfchdir
> posix_spawn_file_actions_addopen
> posix_spawn_file_actions_destroy
> posix_spawn_file_actions_init
> Index: lib/libc/gen/posix_spawn.3
> ===================================================================
> RCS file: /cvs/src/lib/libc/gen/posix_spawn.3,v
> diff -u -p -r1.11 posix_spawn.3
> --- lib/libc/gen/posix_spawn.3 26 Jun 2023 15:28:52 -0000 1.11
> +++ lib/libc/gen/posix_spawn.3 6 Aug 2026 12:18:31 -0000
> @@ -73,8 +73,10 @@ with
> Otherwise, file descriptors in the child process
> are altered according to
> .Xr posix_spawn_file_actions_init 3 ,
> +.Xr posix_spawn_file_actions_addchdir 3 ,
> .Xr posix_spawn_file_actions_addclose 3 ,
> .Xr posix_spawn_file_actions_adddup2 3 ,
> +.Xr posix_spawn_file_actions_addfchdir 3 ,
> and
> .Xr posix_spawn_file_actions_addopen 3 .
> .Pp
> Index: lib/libc/gen/posix_spawn.c
> ===================================================================
> RCS file: /cvs/src/lib/libc/gen/posix_spawn.c,v
> diff -u -p -r1.10 posix_spawn.c
> --- lib/libc/gen/posix_spawn.c 28 Jun 2019 13:32:41 -0000 1.10
> +++ lib/libc/gen/posix_spawn.c 6 Aug 2026 12:18:31 -0000
> @@ -51,7 +51,7 @@ struct __posix_spawn_file_actions {
>
> typedef struct __posix_spawn_file_actions_entry {
> SIMPLEQ_ENTRY(__posix_spawn_file_actions_entry) fae_list;
> - enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE } fae_action;
> + enum { FAE_OPEN, FAE_DUP2, FAE_CLOSE, FAE_CHDIR, FAE_FCHDIR } fae_action;
>
> int fae_fildes;
> union {
> @@ -171,6 +171,14 @@ process_file_actions_entry(posix_spawn_f
> /* Perform a close(), do not fail if already closed */
> (void)close(fae->fae_fildes);
> break;
> + case FAE_CHDIR:
> + if (chdir(fae->fae_path) == -1)
> + return (errno);
> + break;
> + case FAE_FCHDIR:
> + if (fchdir(fae->fae_fildes) == -1)
> + return (errno);
> + break;
> }
> return (0);
> }
> @@ -273,7 +281,8 @@ posix_spawn_file_actions_destroy(posix_s
> SIMPLEQ_REMOVE_HEAD(&(*fa)->fa_list, fae_list);
>
> /* Deallocate file action entry */
> - if (fae->fae_action == FAE_OPEN)
> + if (fae->fae_action == FAE_OPEN ||
> + fae->fae_action == FAE_CHDIR)
> free(fae->fae_path);
> free(fae);
> }
> @@ -352,6 +361,54 @@ posix_spawn_file_actions_addclose(posix_
>
> /* Set values and store in queue */
> fae->fae_action = FAE_CLOSE;
> + fae->fae_fildes = fildes;
> +
> + SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
> + return (0);
> +}
> +
> +int
> +posix_spawn_file_actions_addchdir(posix_spawn_file_actions_t *__restrict fa,
> + const char *__restrict path)
> +{
> + posix_spawn_file_actions_entry_t *fae;
> + int error;
> +
> + /* Allocate object */
> + fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
> + if (fae == NULL)
> + return (errno);
> +
> + /* Set values and store in queue */
> + fae->fae_action = FAE_CHDIR;
> + fae->fae_path = strdup(path);
> + if (fae->fae_path == NULL) {
> + error = errno;
> + free(fae);
> + return (error);
> + }
> +
> + SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
> + return (0);
> +}
> +
> +int
> +posix_spawn_file_actions_addfchdir(posix_spawn_file_actions_t *__restrict fa,
> + int fildes)
> +{
> + posix_spawn_file_actions_entry_t *fae;
> + int error;
> +
> + if (fildes < 0)
> + return (EBADF);
> +
> + /* Allocate object */
> + fae = malloc(sizeof(posix_spawn_file_actions_entry_t));
> + if (fae == NULL)
> + return (errno);
> +
> + /* Set values and store in queue */
> + fae->fae_action = FAE_FCHDIR;
> fae->fae_fildes = fildes;
>
> SIMPLEQ_INSERT_TAIL(&(*fa)->fa_list, fae, fae_list);
> Index: lib/libc/gen/posix_spawn_file_actions_addopen.3
> ===================================================================
> RCS file: /cvs/src/lib/libc/gen/posix_spawn_file_actions_addopen.3,v
> diff -u -p -r1.9 posix_spawn_file_actions_addopen.3
> --- lib/libc/gen/posix_spawn_file_actions_addopen.3 29 Mar 2022 18:15:52 -0000 1.9
> +++ lib/libc/gen/posix_spawn_file_actions_addopen.3 6 Aug 2026 12:18:31 -0000
> @@ -18,17 +18,23 @@
> .Dt POSIX_SPAWN_FILE_ACTIONS_ADDOPEN 3
> .Os
> .Sh NAME
> +.Nm posix_spawn_file_actions_addchdir ,
> .Nm posix_spawn_file_actions_addclose ,
> .Nm posix_spawn_file_actions_adddup2 ,
> +.Nm posix_spawn_file_actions_addfchdir ,
> .Nm posix_spawn_file_actions_addopen
> .Nd add action to close, dup2 or open file descriptor to file actions object
> .Sh SYNOPSIS
> .In spawn.h
> .Ft int
> +.Fn posix_spawn_file_actions_addchdir "posix_spawn_file_actions_t *file_actions" "const char *restrict path"
> +.Ft int
> .Fn posix_spawn_file_actions_addclose "posix_spawn_file_actions_t *file_actions" "int fildes"
> .Ft int
> .Fn posix_spawn_file_actions_adddup2 "posix_spawn_file_actions_t *file_actions" "int fildes" "int newfildes"
> .Ft int
> +.Fn posix_spawn_file_actions_addfchdir "posix_spawn_file_actions_t *file_actions" "int fildes"
> +.Ft int
> .Fn posix_spawn_file_actions_addopen "posix_spawn_file_actions_t *file_actions" "int fildes" "const char *restrict path" "int oflag" "mode_t mode"
> .Sh DESCRIPTION
> These function add an action to
> @@ -45,6 +51,15 @@ Actions are executed in order in the chi
> .Bl -dash
> .It
> The
> +.Fn posix_spawn_file_actions_addchdir
> +function adds an action that causes
> +.Bd -literal -offset indent
> +chdir(path);
> +.Ed
> +.Pp
> +to be called.
> +.It
> +The
> .Fn posix_spawn_file_actions_addclose
> function adds an action that causes
> .Bd -literal -offset indent
> @@ -69,6 +84,15 @@ equals
> .Fa fildes .
> .It
> The
> +.Fn posix_spawn_file_actions_addfchdir
> +function adds an action that causes
> +.Bd -literal -offset indent
> +fchdir(fildes);
> +.Ed
> +.Pp
> +to be called.
> +.It
> +The
> .Fn posix_spawn_file_actions_addopen
> function adds an action that causes
> .Bd -literal -offset indent
> @@ -100,7 +124,12 @@ if they run out of memory.
> .Xr posix_spawn_file_actions_init 3 ,
> .Xr posix_spawnp 3
> .Sh STANDARDS
> -These functions conform to
> +.Fn posix_spawn_file_actions_addchdir
> +and
> +.Fn posix_spawn_file_actions_addfchdir
> +conform to
> +.St -p1003.1-2024 .
> +Other functions conform to
> .St -p1003.1-2001 .
> .Sh AUTHORS
> .An \&Ed Schouten Aq Mt ed@FreeBSD.org
> Index: lib/libc/hidden/spawn.h
> ===================================================================
> RCS file: /cvs/src/lib/libc/hidden/spawn.h,v
> diff -u -p -r1.1 spawn.h
> --- lib/libc/hidden/spawn.h 4 Oct 2015 07:57:21 -0000 1.1
> +++ lib/libc/hidden/spawn.h 6 Aug 2026 12:18:31 -0000
> @@ -21,8 +21,10 @@
> #include_next <spawn.h>
>
> PROTO_DEPRECATED(posix_spawn);
> +PROTO_DEPRECATED(posix_spawn_file_actions_addchdir);
> PROTO_DEPRECATED(posix_spawn_file_actions_addclose);
> PROTO_DEPRECATED(posix_spawn_file_actions_adddup2);
> +PROTO_DEPRECATED(posix_spawn_file_actions_addfchdir);
> PROTO_DEPRECATED(posix_spawn_file_actions_addopen);
> PROTO_DEPRECATED(posix_spawn_file_actions_destroy);
> PROTO_DEPRECATED(posix_spawn_file_actions_init);
>
--
wbr, Kirill
posix_spawn_file_actions_addchdir/addfchdir