aboutsummaryrefslogblamecommitdiffstats
path: root/libexec.h
blob: 84928904eb7760e31cbc3c4ab974f0873430b935 (plain) (tree)
1
2
3
4
5
6
7
8
9







                                                         
                  




                   
                   

 
                                                            










                                                                                                     






                                           


       








                                               


                          











                                              
                          



                                                              
                                                                 



































                                                                                                  

  



                                                    
                           




                                                    
                               





                                                     
                                



                                           
                              




                                               
                             







                                                   
                                 









                                                        
                            

                                                                                                   

  








                                                             
                   


                                                           
                         



                                                                  
                                



                                                                     
                                   



                                                                 
                               




                                                               
                                






                                                               
                                        

                                                                                           

  



                                                  
                            








                                                     
                           





                                                      
                             





                                                       
                        








                                                      
                       

                                                                                                    

  
 































                                                                                                                  
                                                                                                       










































































                                                                                                                              





























                                                             


                                                                                                         





                                                                                           

                                                                                       
                                                                                               










                                                                                      





                                                                                                                               
                                                                                                             

                                                                                                      







                                                                                                                         















                                                                                         






















































































































































































































































































                                                                                                                                    


                                                                                                



















































































































                                                                                                                                   


                                                                                                                        






















                                                                                        


                                                                       


























                                                                                           


                                                                                    


























                                                                                           


                                                                          






















                                                                                        


                                                                                        

























                                                                                           


                                                                                                            

























                                                                                           


                                                                                                  

























                                                                                        


                                                                         





























                                                                                           


                                                                                      





























                                                                                           


                                                                            

























                                                                                        


                                                                                          




























                                                                                           


                                                                                                              




























                                                                                           


                                                                                                    

























                                                                                              


                                                                         





























                                                                                              


                                                                                      





























                                                                                              


                                                                            

























                                                                                              


                                                                                          




























                                                                                              


                                                                                                              




























                                                                                              


                                                                                                    






















                                                                                        


                                                                      

























                                                                                           


                                                                                   

























                                                                                           


                                                                         






















                                                                                        


                                                                                       

























                                                                                           


                                                                                                           

























                                                                                           


                                                                                                 






















                                                                                        


                                                                       

























                                                                                           


                                                                                    

























                                                                                           


                                                                          






















                                                                                        


                                                                                        

























                                                                                           


                                                                                                            

























                                                                                           


                                                                                                  

































                                                                              
                                                                                        































                                                                              











                                                                                  































                                                                              
                                                       































                                                                             


                                                        




































                                                                                                                            

                                                       


      
/* See LICENSE file for copyright and license details. */
#ifndef LIBEXEC_H
#define LIBEXEC_H


#if defined(__linux__)
# include <linux/openat2.h>
#endif
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>


/* For internal use only (may be removed in the future) { */
#if defined(__GNUC__)
# define LIBEXEC_PURE__ __attribute__((__pure__))
# define LIBEXEC_CONST__ __attribute__((__const__))
# define LIBEXEC_PRINTF__(FMTIDX) __attribute__((__format__(__gnu_printf__, (FMTIDX), (FMTIDX) + 1)))
# define LIBEXEC_VPRINTF__(FMTIDX) __attribute__((__format__(__gnu_printf__, (FMTIDX), 0)))
#else
# define LIBEXEC_PURE__
# define LIBEXEC_CONST__
# define LIBEXEC_PRINTF__(FMTIDX)
# define LIBEXEC_VPRINTF__(FMTIDX)
#endif
#define LIBEXEC_VA_IMPL__(LAST, FUNC, ...)\
	int ret;\
	va_list args;\
	va_start(args, LAST);\
	ret = FUNC(__VA_ARGS__, args);\
	va_end(args);\
	return ret
/* } */


/**
 * Used to specify which version of the library
 * the application is compiled against, so that
 * the library knowns the layout of structures
 * that may be modified in future versions
 * 
 * This is currently used for:
 * - `struct libexec_command`
 */
#define LIBEXEC_VERSION 0U


/**
 * Specifies how an element should be inserted
 * into a list
 * 
 * This is currently used for:
 * - `libexec_putenv`
 * - `libexec_putenvf`
 * - `libexec_vputenvf`
 * - `libexec_setenv`
 * - `libexec_setenvf`
 * - `libexec_vsetenvf`
 */
enum libexec_insert_mode {
	/**
	 * Append to list, but if the an element with the same
	 * key is used, replace the first occurrence of it
	 */
	LIBEXEC_REPLACE, /* replace existing definition if any */

	/**
	 * Append to list except if the an element with the same
	 * key is used, then do nothing
	 */
	LIBEXEC_NOREPLACE,

	/**
	 * Append to list except if the an element with the same
	 * key is used, then do nothing but indicate fail
	 */
	LIBEXEC_NOCLOBBER,

	/**
	 * Add to the end of the list regardless of whether
	 * there already is an element with the same key in he list
	 * 
	 * If used in `libexec_putenv`, `libexec_putenvf`, or
	 * `libexec_vputenvf`, that function will accept the
	 * new element even if it doesn't use a key–value
	 * construction
	 */
	LIBEXEC_APPEND,

	/**
	 * Add to the beginning of the list regardless of whether
	 * there already is an element with the same key in he list
	 * 
	 * If used in `libexec_putenv`, `libexec_putenvf`, or
	 * `libexec_vputenvf`, that function will accept the
	 * new element even if it doesn't use a key–value
	 * construction
	 */
	LIBEXEC_PREPEND

#define LIBEXEC_INSERT_MODE__COUNT__  5 /* For internal use only (may be removed in the future) */
};


/**
 * Modification to command's set of file descriptors
 */
enum libexec_pluming_type {
	/**
	 * Perform an openat(3) call, and change the
	 * number of the new file descriptor to a
	 * desired number
	 */
	LIBEXEC_PLUMING_OPENAT,

	/**
	 * Perform an openat2(2) call, and change the
	 * number of the new file descriptor to a
	 * desired number
	 */
	LIBEXEC_PLUMING_OPENAT2,

	/**
	 * Close a specific file descriptor
	 */
	LIBEXEC_PLUMING_CLOSE,

	/**
	 * Perform an dup2(3) call to duplicate
	 * a file descriptor
	 */
	LIBEXEC_PLUMING_DUP2,

	/**
	 * Pass text from the application's memory
	 * into the child process via a pipe, whose
	 * read-end will have a selected number;
	 * the write-end will not be exposed to the
	 * child process
	 */
	LIBEXEC_PLUMING_DOCUMENT,

	/**
	 * Expose a file descriptor from the application
	 * using a selected file descriptor number
	 * 
	 * Note that unless a file descriptor from the
	 * application is explicitly close it will
	 * still be exposed to the child process; this
	 * merely changes the file descriptor number
	 */
	LIBEXEC_PLUMING_PIPE

#define LIBEXEC_PLUMING_TYPE__COUNT__  6 /* For internal use only (may be removed in the future) */
};


/**
 * Specifies how two commands shall be connected
 *
 * The value should be `LIBEXEC_PIPE`, `LIBEXEC_SOCK_STREAM`,
 * `LIBEXEC_SOCK_SEQPACKET`, `LIBEXEC_SOCK_DGRAM, or
 * `LIBEXEC_DIRECT_PIPE`, optionally OR'ed with
 * `LIBEXEC_PIPE_CIRCULARLY`
 */
enum libexec_pipe {
	/**
	 * Use a normal pipe(7) to connect the two commands
	 */
	LIBEXEC_PIPE = 0,

	/**
	 * Use a unix(7) socketpair(3) with the type `SOCK_STREAM`
	 */
	LIBEXEC_SOCK_STREAM = 1,

	/**
	 * Use a unix(7) socketpair(3) with the type `SOCK_SEQPACKET`
	 */
	LIBEXEC_SOCK_SEQPACKET = 2,

	/**
	 * Use a unix(7) socketpair(3) with the type `SOCK_DGRAM`
	 */
	LIBEXEC_SOCK_DGRAM = 3,

	/**
	 * Use a pipe(7) created with `O_DIRECT` (see pipe2(2))
	 * to connect the two commands
	 */
	LIBEXEC_DIRECT_PIPE = 4,

	/**
	 * Pipe the standard output of the last command to
	 * the standard input of the first command, in addition
	 * to piping the standard output of each command to
	 * the standard input of the next command
	 */
	LIBEXEC_PIPE_CIRCULARLY = 0x8000

#define LIBEXEC_PIPE__COUNT__  5 /* For internal use only (may be removed in the future) */
};


/**
 * Specifies which exec(3) function should be used
 */
enum libexec_locate_method {
	/**
	 * p-class exec(3) function shall be used,
	 * that is, the executable may either be
	 * a relative or absolute path, but it may
	 * also be the name of a program that can
	 * be found in PATH. PATH is used, if and
	 * only if, the specified executable contains
	 * a slash
	 */
	LIBEXEC_ALLOW_NAME,

	/**
	 * non-p-class exec(3) function shall be used,
	 * that is, the executable must be a relative
	 * or absolute path
	 */
	LIBEXEC_REQUIRE_PATH,

	/**
	 * f-class exec(3) function shall be used,
	 * that is, a file descriptor to the executable
	 * is used
	 */
	LIBEXEC_EXEC_FD,

	/**
	 * at-class exec(3) function shall be used,
	 * that is, a function like execveat(2),
	 * where the executable is specified via
	 * a directory file descriptor (or `AT_FDCWD`)
	 * paired with a pathname, and AT_-flags,
	 * is used
	 */
	LIBEXEC_EXEC_AT

#define LIBEXEC_LOCATE_METHOD__COUNT__  4 /* For internal use only (may be removed in the future) */
};


enum libexec_run_instruction {
	LIBEXEC_RUN_END, /* required to mark end if arguments for libexec_run */
	LIBEXEC_RUN_END_CIRCULAR_PIPE, /* alternative to LIBEXEC_RUN_END */
	LIBEXEC_RUN_PIPE,
	LIBEXEC_RUN_SET_EXECUTABLE,
	LIBEXEC_RUN_SET_REQUIRE_PATH,
	LIBEXEC_RUN_SET_EXEC_FD,
	LIBEXEC_RUN_SET_EXEC_PATH,
	LIBEXEC_RUN_COPY_ENVIRON,
	LIBEXEC_RUN_SET_ENVIRON,
	LIBEXEC_RUN_CLEAR_ENVIRON,
	LIBEXEC_RUN_UNSETENV,
	LIBEXEC_RUN_PUTENV,
	LIBEXEC_RUN_SETENV,
	LIBEXEC_RUN_PUTENVF,
	LIBEXEC_RUN_SETENVF,
	LIBEXEC_RUN_OPEN,
	LIBEXEC_RUN_OPENAT,
	LIBEXEC_RUN_OPENAT2, /* generates ENOSYS unless running on Linux */
	LIBEXEC_RUN_DUP,
	LIBEXEC_RUN_CLOSE,
	LIBEXEC_RUN_RENUMBER_FD,
	LIBEXEC_RUN_INPUT_COPY,
	LIBEXEC_RUN_INPUT_GIFT,
	LIBEXEC_RUN_ADD_OUTPUT_FD, /* use LIBEXEC_RUN_SET_FD_CALLBACK first */
	LIBEXEC_RUN_ADD_OUTPUT,	
	LIBEXEC_RUN_SET_SIGMASK, /* use once to configure how libexec_run_pipeline is invoked behind the scenes */
	LIBEXEC_RUN_SET_FD_CALLBACK, /* ditto */
	LIBEXEC_RUN_SET_REAPER, /* ditto */
	LIBEXEC_RUN_SET_ATFORK, /* ditto */
	LIBEXEC_RUN_SET_MUTEX, /* ditto */
	LIBEXEC_RUN_SET_INTERRUPT_CALLBACK /* ditto */
#define LIBEXEC_RUN_INSTRUCTION__COUNT__  31 /* For internal use only (may be removed in the future) */
};
#define LIBEXEC_RUN_END() LIBEXEC_RUN_END
#define LIBEXEC_RUN_END_CIRCULAR_PIPE(TYPE) LIBEXEC_RUN_END_CIRCULAR_PIPE, TYPE
#define LIBEXEC_RUN_PIPE(TYPE) LIBEXEC_RUN_PIPE, TYPE
#define LIBEXEC_RUN_SET_EXECUTABLE(FILE) LIBEXEC_RUN_SET_EXECUTABLE, FILE
#define LIBEXEC_RUN_SET_REQUIRE_PATH(REQUIRE) LIBEXEC_RUN_SET_REQUIRE_PATH, REQUIRE
#define LIBEXEC_RUN_SET_EXEC_FD(FD) LIBEXEC_RUN_SET_EXEC_FD, FD
#define LIBEXEC_RUN_SET_EXEC_PATH(DIRFD, PATH) LIBEXEC_RUN_SET_EXEC_PATH, DIRFD, PATH
#define LIBEXEC_RUN_COPY_ENVIRON(ENVIRON) LIBEXEC_RUN_COPY_ENVIRON, ENVIRON
#define LIBEXEC_RUN_SET_ENVIRON(ENVIRON) LIBEXEC_RUN_SET_ENVIRON, ENVIRON
#define LIBEXEC_RUN_CLEAR_ENVIRON(ENVIRON) LIBEXEC_RUN_CLEAR_ENVIRON
#define LIBEXEC_RUN_UNSETENV(ENV) LIBEXEC_RUN_UNSETENV, ENV
#define LIBEXEC_RUN_PUTENV(... /* STR, [HOW] */) LIBEXEC_RUN_PUTENV__(__VA_ARGS__, LIBEXEC_REPLACE,)
#define LIBEXEC_RUN_SETENV(... /* ENV, VAL, [HOW] */) LIBEXEC_RUN_PUTENV__(__VA_ARGS__, LIBEXEC_REPLACE,)
#define LIBEXEC_RUN_PUTENV_SAFE(HOW, STR) LIBEXEC_RUN_PUTENV, HOW, STR
#define LIBEXEC_RUN_SETENV_SAFE(HOW, ENV, VAL) LIBEXEC_RUN_SETENV, HOW, ENV, VAL
#define LIBEXEC_RUN_PUTENVF_SAFE(HOW, ...) LIBEXEC_RUN_PUTENV, HOW, __VA_ARGS__
#define LIBEXEC_RUN_SETENVF_SAFE(HOW, ENV, ...) LIBEXEC_RUN_SETENV, HOW, ENV, __VA_ARGS__
#define LIBEXEC_RUN_PUTENVF(...) LIBEXEC_RUN_PUTENVF_SAFE(LIBEXEC_REPLACE, __VA_ARGS__)
#define LIBEXEC_RUN_SETENVF(ENV, ...) LIBEXEC_RUN_SETENVF_SAFE(LIBEXEC_REPLACE, ENV, __VA_ARGS__)
#define LIBEXEC_RUN_OPEN(FD, FILE, ... /* FLAGS, [MODE] */) LIBEXEC_RUN_OPEN__(FD, FILE, __VA_ARGS__, 0666,)
#define LIBEXEC_RUN_OPENAT(FD, DIRFD, FILE, ... /* FLAGS, [MODE] */) LIBEXEC_RUN_OPENAT__(FD, DIRFD, FILE, __VA_ARGS__, 0666,)
#define LIBEXEC_RUN_OPENAT2(FD, DIRFD, FILE, ... /* HOW, [SIZE] */)\
	LIBEXEC_RUN_OPENAT2__(FD, DIRFD, FILE, __VA_ARGS__, sizeof(LIBEXEC__FIRST__(__VA_ARGS__,)),)
#define LIBEXEC_RUN_DUP(NEW_FD, OLD_FD) LIBEXEC_RUN_DUP, NEW_FD, OLD_FD
#define LIBEXEC_RUN_CLOSE(FD) LIBEXEC_RUN_CLOSE, FD
#define LIBEXEC_RUN_RENUMBER_FD(NEW_FD, OLD_FD) LIBEXEC_RUN_RENUMBER_FD, NEW_FD, OLD_FD
#define LIBEXEC_RUN_INPUT_COPY(FD, ... /* DATA, [LEN] */)\
	LIBEXEC_RUN_INPUT__(COPY, FD, __VA_ARGS__, strlen(LIBEXEC__FIRST__(__VA_ARGS__)),)
#define LIBEXEC_RUN_INPUT_GIFT(FD, ... /* DATA, [LEN] */)\
	LIBEXEC_RUN_INPUT__(GIFT, FD, __VA_ARGS__, strlen(LIBEXEC__FIRST__(__VA_ARGS__)),)
#define LIBEXEC_RUN_ADD_OUTPUT_FD(FD, WR_FD) LIBEXEC_RUN_ADD_OUTPUT_FD, FD, WR_FD
#define LIBEXEC_RUN_ADD_OUTPUT(FD, OUT) LIBEXEC_RUN_ADD_OUTPUT, FD, OUT
#define LIBEXEC_RUN_SET_SIGMASK(SIGMASK) LIBEXEC_RUN_SET_SIGMASK, SIGMASK
#define LIBEXEC_RUN_SET_FD_CALLBACK(CB, USER) LIBEXEC_RUN_SET_FD_CALLBACK, CB, USER
#define LIBEXEC_RUN_SET_REAPER(CB, USER) LIBEXEC_RUN_SET_REAPER, CB, USER
#define LIBEXEC_RUN_SET_ATFORK(CB, USER) LIBEXEC_RUN_SET_ATFORK, CB, USER
#define LIBEXEC_RUN_SET_MUTEX(CB, USER) LIBEXEC_RUN_SET_MUTEX, CB, USER
#define LIBEXEC_RUN_SET_INTERRUPT_CALLBACK(CB, USER) LIBEXEC_RUN_SET_INTERRUPT_CALLBACK, CB, USER

#define LIBEXEC__FIRST__(X, ...) X
#define LIBEXEC_RUN_PUTENV__(STR, HOW, ...) LIBEXEC_RUN_PUTENV, HOW, STR
#define LIBEXEC_RUN_SETENV__(ENV, VAL, HOW, ...) LIBEXEC_RUN_SETENV, HOW, ENV, VAL
#define LIBEXEC_RUN_OPEN__(FD, FILE, FLAGS, MODE, ...) LIBEXEC_RUN_OPEN, FD, FILE, FLAGS, MODE
#define LIBEXEC_RUN_OPENAT__(FD, DIRFD, FILE, FLAGS, MODE, ...) LIBEXEC_RUN_OPENAT, FD, DIRFD, FILE, FLAGS, MODE
#define LIBEXEC_RUN_INPUT__(INSTR, FD, DATA, LEN, ...) LIBEXEC_RUN_INPUT_##INSTR, FD, DATA, LEN

struct libexec_pluming {
	int fd;
	enum libexec_pluming_type type;
	union {
		int fd;
		struct {
			char *file;
			int dirfd;
			union {
				struct {
					mode_t mode;
					int flags;
				};
#if defined(__linux__)
				struct {
					struct open_how *how;
					size_t how_size;
				};
#endif
			};
		};
		struct {
			char *text;
			size_t len;
		};
	} target;
};

struct libexec_command {
	unsigned library_version;
	enum libexec_locate_method exec_how;
	int exec_fd;
	char *executable;
	char **arguments;
	size_t narguments;
	struct libexec_pluming *plumings;
	size_t nplumings;
	char **environ;
};

struct libexec_document {
	unsigned long long int user;
	int fd;
	char *text;
	size_t length;
	union {
		size_t offset; /* when used for input */
		size_t alloc_size; /* when used for output */
	};
};

struct libexec_result {
	int *exit_statuses;
	size_t proc_count;
	int all_successful;
};


#define LIBEXEC_COMMAND_INIT\
	((struct libexec_command){LIBEXEC_VERSION, LIBEXEC_ALLOW_NAME, -1, NULL, NULL, 0, NULL, 0, NULL})

void libexec_destroy_command(struct libexec_command *cmd);
void libexec_destroy_pluming(struct libexec_pluming *pluming);
int libexec_destroy_document(struct libexec_document *doc);
void libexec_destroy_result(struct libexec_result *result);

int libexec_vconstruct_command(struct libexec_command *cmd, const char *fmt, va_list args);
inline int libexec_construct_command(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vconstruct_command, cmd, fmt); }
int libexec_put_argumentsn(struct libexec_command *cmd, const char *const *args, size_t nargs);
inline int libexec_put_arguments(struct libexec_command *cmd, const char *const *args)
{
	size_t n = 0;
	if (!args) {
		errno = EINVAL;
		return -1;
	}
	while (args[n])
		n += 1;
	return libexec_put_argumentsn(cmd, args, n);
}
int libexec_set_executable(struct libexec_command *cmd, const char *executable /* NULL = use first argument in command */);
void libexec_set_require_path(struct libexec_command *cmd, int require);
int libexec_set_exec_fd(struct libexec_command *cmd, int fd);
int libexec_set_exec_path(struct libexec_command *cmd, int dirfd, const char *path /* NULL = use first argument in command */);

int libexec_add_pluming(struct libexec_command *cmd, const struct libexec_pluming *pluming);
int libexec_openat(struct libexec_command *cmd, int fd, int dirfd, const char *file, int flags, mode_t mode);
inline int libexec_open(struct libexec_command *cmd, int fd, const char *file, int flags, mode_t mode)
{ return libexec_openat(cmd, fd, AT_FDCWD, file, flags, mode); }
#if defined(__linux__)
int libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, struct open_how *how, size_t size);
#endif
int libexec_dup(struct libexec_command *cmd, int fd, int old_fd);
int libexec_close(struct libexec_command *cmd, int fd);
int libexec_renumber_fd(struct libexec_command *cmd, int fd, int old_fd);
int libexec_input_data_copy(struct libexec_command *cmd, int fd, const char *data, size_t len);
int libexec_input_data_gift(struct libexec_command *cmd, int fd, char *data, size_t len);
inline int libexec_input_text_copy(struct libexec_command *cmd, int fd, const char *text)
{
	if (!text) {
		errno = EINVAL;
		return -1;
	}
	return libexec_input_data_copy(cmd, fd, text, strlen(text));
}
inline int libexec_input_text_gift(struct libexec_command *cmd, int fd, char *text)
{
	if (!text) {
		errno = EINVAL;
		return -1;
	}
	return libexec_input_data_gift(cmd, fd, text, strlen(text));
}
int libexec_add_output_fd(struct libexec_command *cmd, int fd, int wr_fd);
int libexec_add_output_document(struct libexec_command *cmd, int fd, struct libexec_document *doc /* .fd will be set */, int flags);


/**
 * The the value of an environment variable in a command
 * 
 * If command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment is inspected
 * 
 * In the event that the environment contains multiple
 * definitions of the same variable, the first definition
 * is used
 * 
 * @param  cmd   The command whose environment shall be inspected
 * @param  name  The name of the environment variable to read
 * @return       The value of the environment variable, or NULL
 *               on failure or if the environment variable was
 *               not found
 * 
 * @throws  (unmodified)  The environment variable does not exist
 * @throws  EINVAL        Invalid argument input
 */
LIBEXEC_PURE__ const char *libexec_getenv(struct libexec_command *, const char *);

/**
 * The the value of an environment variable in a command
 * 
 * If command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment is inspected
 * 
 * In the event that the environment contains multiple
 * definitions of the same variable, the last definition
 * is used
 * 
 * @param  cmd   The command whose environment shall be inspected
 * @param  name  The name of the environment variable to read
 * @return       The value of the environment variable, or NULL
 *               on failure or if the environment variable was
 *               not found
 * 
 * @throws  (unmodified)  The environment variable does not exist
 * @throws  EINVAL        Invalid argument input
 */
LIBEXEC_PURE__ const char *libexec_getenv_last(struct libexec_command *, const char *);


/**
 * Copy a list of environment variables and used it for a command
 * 
 * @param  cmd  The command whose environment shall be set
 * @param  env  The environment (will be copied), if NULL, the
 *              calling process's environment will be (copied
 *              and) used for the command
 * @return      0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
int libexec_copy_environ(struct libexec_command *, const char *const *);

/**
 * Set the command's environment to a prebuilt list of
 * environment variables
 *
 * @param  cmd  The command whose environment shall be set
 * @param  env  The environment; the ownership of the input
 *              pointer and all pointers stored in it will be
 *              transfered to `cmd`, meaning that they will be
 *              deallocated using free(3) when they are removed
 *              or when `cmd` is destructed. If NULL, the
 *              commands environment will be reset to its default
 *              state, meaning that the calling process's environment
 *              (it will not be copied into `cmd`) will be used
 *              when running the command
 */
void libexec_set_environ(struct libexec_command *, char **);

/**
 * Clear a command's environment, removing all it's environment variables
 * 
 * Even if the command's environment is in its default state (to
 * use the calling process's environment), and calling process's
 * environment is void of variables, the command's environment
 * will be put in a non-default state, meaning that future changes
 * to the calling process's environments variable will not affect
 * the command's environment
 * 
 * @param   cmd  The command whose environment shall be modified
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
int libexec_clear_environ(struct libexec_command *);

/**
 * Remove all occurences of an environment variable
 * in a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will be copied and set as the
 * command's environment once the variable is found; however
 * if the variable is not found within the environment, the
 * command's environment will remain in its default state
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   name  The name of the variable to remove from the environment
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
int libexec_unsetenv(struct libexec_command *, const char *);

/**
 * Remove the first occurence of an environment variable
 * in a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will be copied and set as the
 * command's environment once the variable is found; however
 * if the variable is not found within the environment, the
 * command's environment will remain in its default state
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   name  The name of the variable to remove from the environment
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
int libexec_unsetenv_first(struct libexec_command *, const char *);

/**
 * Remove the last occurence of an environment variable
 * in a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will be copied and set as the
 * command's environment once the variable is found; however
 * if the variable is not found within the environment, the
 * command's environment will remain in its default state
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   name  The name of the variable to remove from the environment
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
int libexec_unsetenv_last(struct libexec_command *, const char *);


/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   how     Must be one of the following values:
 *                  - LIBEXEC_REPLACE:
 *                      If the environment already contains the
 *                      variable, replace its first definition in
 *                      the environment
 *                  - LIBEXEC_NOREPLACE:
 *                      Do not replacing already existing definition
 *                      of the environment variable
 *                  - LIBEXEC_NOCLOBBER:
 *                      Fail if the environment already contains the
 *                      variable, but has a different value
 *                  - LIBEXEC_APPEND:
 *                      Always the string to the end of the environment
 *                  - LIBEXEC_PREPEND:
 *                      Always the string to the beginning of the environment
 * @param   string  The environment variable that should be put into
 *                  the environment, unless `how` is `LIBEXEC_APPEND`
 *                  or `LIBEXEC_PREPEND`, it must be in the form
 *                  `"%s=%s", name, value` (where `name` does not
 *                  contain an equal sign)
 * @return          0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 */
int libexec_putenv(struct libexec_command *, enum libexec_insert_mode, const char *);

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   how   Must be one of the following values:
 *                - LIBEXEC_REPLACE:
 *                    If the environment already contains the
 *                    variable, replace its first definition in
 *                    the environment
 *                - LIBEXEC_NOREPLACE:
 *                    Do not replacing already existing definition
 *                    of the environment variable
 *                - LIBEXEC_NOCLOBBER:
 *                    Fail if the environment already contains the
 *                    variable, but has a different value
 *                - LIBEXEC_APPEND:
 *                    Always the string to the end of the environment
 *                - LIBEXEC_PREPEND:
 *                    Always the string to the beginning of the environment
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment, unless `how` is `LIBEXEC_APPEND`
 *                or `LIBEXEC_PREPEND`, the resulting string must
 *                be in the form `"%s=%s", name, value` (where `name`
 *                does not contain an equal sign)
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_VPRINTF__(3) int libexec_vputenvf(struct libexec_command *, enum libexec_insert_mode, const char *, va_list);

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   how   Must be one of the following values:
 *                - LIBEXEC_REPLACE:
 *                    If the environment already contains the
 *                    variable, replace its first definition in
 *                    the environment
 *                - LIBEXEC_NOREPLACE:
 *                    Do not replacing already existing definition
 *                    of the environment variable
 *                - LIBEXEC_NOCLOBBER:
 *                    Fail if the environment already contains the
 *                    variable, but has a different value
 *                - LIBEXEC_APPEND:
 *                    Always the string to the end of the environment
 *                - LIBEXEC_PREPEND:
 *                    Always the string to the beginning of the environment
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment, unless `how` is `LIBEXEC_APPEND`
 *               or `LIBEXEC_PREPEND`, the resulting string must
 *               be in the form `"%s=%s", name, value` (where `name`
 *               does not contain an equal sign)
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_putenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf, cmd, how, fmt); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   how    Must be one of the following values:
 *                 - LIBEXEC_REPLACE:
 *                     If the environment already contains the
 *                     variable, replace its first definition in
 *                     the environment
 *                 - LIBEXEC_NOREPLACE:
 *                     Do not replacing already existing definition
 *                     of the environment variable
 *                 - LIBEXEC_NOCLOBBER:
 *                     Fail if the environment already contains the
 *                     variable, but has a different value
 *                 - LIBEXEC_APPEND:
 *                     Always the string to the end of the environment
 *                 - LIBEXEC_PREPEND:
 *                     Always the string to the beginning of the environment
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 */
int libexec_setenv(struct libexec_command *, enum libexec_insert_mode, const char *, const char *);

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   how        Must be one of the following values:
 *                     - LIBEXEC_REPLACE:
 *                         If the environment already contains the
 *                         variable, replace its first definition in
 *                         the environment
 *                     - LIBEXEC_NOREPLACE:
 *                         Do not replacing already existing definition
 *                         of the environment variable
 *                     - LIBEXEC_NOCLOBBER:
 *                         Fail if the environment already contains the
 *                         variable, but has a different value
 *                     - LIBEXEC_APPEND:
 *                         Always the string to the end of the environment
 *                     - LIBEXEC_PREPEND:
 *                         Always the string to the beginning of the environment
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_VPRINTF__(4) int libexec_vsetenvf(struct libexec_command *, enum libexec_insert_mode, const char *, const char *, va_list);

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   how        Must be one of the following values:
 *                     - LIBEXEC_REPLACE:
 *                         If the environment already contains the
 *                         variable, replace its first definition in
 *                         the environment
 *                     - LIBEXEC_NOREPLACE:
 *                         Do not replacing already existing definition
 *                         of the environment variable
 *                     - LIBEXEC_NOCLOBBER:
 *                         Fail if the environment already contains the
 *                         variable, but has a different value
 *                     - LIBEXEC_APPEND:
 *                         Always the string to the end of the environment
 *                     - LIBEXEC_PREPEND:
 *                         Always the string to the beginning of the environment
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 *                        (Only if `how` is `LIBEXEC_NOCLOBBER`)
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_PRINTF__(4) inline int
libexec_setenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf, cmd, how, name, value_fmt); }


/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenv(cmd, LIBEXEC_REPLACE, string)`
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   string  The environment variable that should be put into the
 *                  environment, it must be in the form `"%s=%s", name, value`
 *                  (where `name` does not contain an equal sign)
 * @return          0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_putenv_replace(struct libexec_command *cmd, const char *string)
{ return libexec_putenv(cmd, LIBEXEC_REPLACE, string); }

/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_REPLACE, fmt, args)`
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment, the resulting string must
 *                be in the form `"%s=%s", name, value` (where `name`
 *                does not contain an equal sign)
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(2) inline int
libexec_vputenvf_replace(struct libexec_command *cmd, const char *fmt, va_list args)
{ return libexec_vputenvf(cmd, LIBEXEC_REPLACE, fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenvf(cmd, LIBEXEC_REPLACE, fmt, ...)`
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment, the resulting string must
 *               be in the form `"%s=%s", name, value` (where `name`
 *               does not contain an equal sign)
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(2) inline int
libexec_putenvf_replace(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf_replace, cmd, fmt); }

/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenv(cmd, LIBEXEC_REPLACE, name, value)`
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_setenv_replace(struct libexec_command *cmd, const char *name, const char *value)
{ return libexec_setenv(cmd, LIBEXEC_REPLACE, name, value); }

/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, args)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(3) inline int
libexec_vsetenvf_replace(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args)
{ return libexec_vsetenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * replacing the first already existing definition of the
 * variable if there is one
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenvf(cmd, LIBEXEC_REPLACE, name, value_fmt, ...)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_setenvf_replace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf_replace, cmd, name, value_fmt); }


/**
 * Put an environment variable into a command's environment
 *
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_putenv(cmd, LIBEXEC_NOREPLACE, string)`
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   string  The environment variable that should be put into the
 *                  environment, it must be in the form `"%s=%s", name, value`
 *                  (where `name` does not contain an equal sign)
 * @return          0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_putenv_noreplace(struct libexec_command *cmd, const char *string)
{ return libexec_putenv(cmd, LIBEXEC_NOREPLACE, string); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_NOREPLACE, fmt, args)`
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment, the resulting string must
 *                be in the form `"%s=%s", name, value` (where `name`
 *                does not contain an equal sign)
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(2) inline int
libexec_vputenvf_noreplace(struct libexec_command *cmd, const char *fmt, va_list args)
{ return libexec_vputenvf(cmd, LIBEXEC_NOREPLACE, fmt, args); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_putenvf(cmd, LIBEXEC_NOREPLACE, fmt, ...)`
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment, the resulting string must
 *               be in the form `"%s=%s", name, value` (where `name`
 *               does not contain an equal sign)
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(2) inline int
libexec_putenvf_noreplace(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf_noreplace, cmd, fmt); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_setenv(cmd, LIBEXEC_NOREPLACE, name, value)`
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_setenv_noreplace(struct libexec_command *cmd, const char *name, const char *value)
{ return libexec_setenv(cmd, LIBEXEC_NOREPLACE, name, value); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, args)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(3) inline int
libexec_vsetenvf_noreplace(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args)
{ return libexec_vsetenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, args); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, the command's environment variable will not
 * be modified, except that initialisation from default state
 * as describe above will take place
 * 
 * Equivalent to `libexec_setenvf(cmd, LIBEXEC_NOREPLACE, name, value_fmt, ...)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_setenvf_noreplace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf_noreplace, cmd, name, value_fmt); }


/**
 * Put an environment variable into a command's environment
 *
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_putenv(cmd, LIBEXEC_NOCLOBBER, string)`
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   string  The environment variable that should be put into the
 *                  environment, it must be in the form `"%s=%s", name, value`
 *                  (where `name` does not contain an equal sign)
 * @return          0 on success, -1 on failure
 *
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_putenv_noclobber(struct libexec_command *cmd, const char *string)
{ return libexec_putenv(cmd, LIBEXEC_NOCLOBBER, string); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_NOCLOBBER, fmt, args)`
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment, the resulting string must
 *                be in the form `"%s=%s", name, value` (where `name`
 *                does not contain an equal sign)
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_VPRINTF__(2) inline int
libexec_vputenvf_noclobber(struct libexec_command *cmd, const char *fmt, va_list args)
{ return libexec_vputenvf(cmd, LIBEXEC_NOCLOBBER, fmt, args); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_putenvf(cmd, LIBEXEC_NOCLOBBER, fmt, ...)`
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment, the resulting string must
 *               be in the form `"%s=%s", name, value` (where `name`
 *               does not contain an equal sign)
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_PRINTF__(2) inline int
libexec_putenvf_noclobber(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf_noclobber, cmd, fmt); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_setenv(cmd, LIBEXEC_NOCLOBBER, name, value)`
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_setenv_noclobber(struct libexec_command *cmd, const char *name, const char *value)
{ return libexec_setenv(cmd, LIBEXEC_NOCLOBBER, name, value); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, args)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_VPRINTF__(3) inline int
libexec_vsetenvf_noclobber(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args)
{ return libexec_vsetenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, args); }

/**
 * Put an environment variable into a command's environment
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * If the environment variable already exists in the command's
 * environment, but has a different value, the function will
 * fail without side effects
 * 
 * Equivalent to `libexec_setenvf(cmd, LIBEXEC_NOCLOBBER, name, value_fmt, ...)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  (unmodified)  The environment variable already exists
 * @throws  EINVAL        Invalid argument input
 * @throws  ENOMEM        Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ        According to printf(3)
 * @throws  EOVERFLOW     According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_setenvf_noclobber(struct libexec_command *cmd, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf_noclobber, cmd, name, value_fmt); }


/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenv(cmd, LIBEXEC_APPEND, string)`
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   string  The environment variable that should be put into the
 *                  environment; there is no requirement that it contains
 *                  an equal sign
 * @return          0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_putenv_append(struct libexec_command *cmd, const char *string)
{ return libexec_putenv(cmd, LIBEXEC_APPEND, string); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_APPEND, fmt, args)`
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment; there is no requirement that
 *                the resulting string contains an equal sign
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(2) inline int
libexec_vputenvf_append(struct libexec_command *cmd, const char *fmt, va_list args)
{ return libexec_vputenvf(cmd, LIBEXEC_APPEND, fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenvf(cmd, LIBEXEC_APPEND, fmt, ...)`
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment; there is no requirement that
 *               the resulting string contains an equal sign
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(2) inline int
libexec_putenvf_append(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf_append, cmd, fmt); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenv(cmd, LIBEXEC_APPEND, name, value)`
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_setenv_append(struct libexec_command *cmd, const char *name, const char *value)
{ return libexec_setenv(cmd, LIBEXEC_APPEND, name, value); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_APPEND, name, value_fmt, args)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(3) inline int
libexec_vsetenvf_append(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args)
{ return libexec_vsetenvf(cmd, LIBEXEC_APPEND, name, value_fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the end of the environment regardless of
 * whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenvf(cmd, LIBEXEC_APPEND, name, value_fmt, ...)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_setenvf_append(struct libexec_command *cmd, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf_append, cmd, name, value_fmt); }


/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenv(cmd, LIBEXEC_PREPEND, string)`
 * 
 * @param   cmd     The command whose environment shall be modified
 * @param   string  The environment variable that should be put into the
 *                  environment; there is no requirement that it contains
 *                  an equal sign
 * @return          0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_putenv_prepend(struct libexec_command *cmd, const char *string)
{ return libexec_putenv(cmd, LIBEXEC_PREPEND, string); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vputenvf(cmd, LIBEXEC_PREPEND, fmt, args)`
 * 
 * @param   cmd   The command whose environment shall be modified
 * @param   fmt   Formatting string, following the rules of printf(3),
 *                for the environment variable that should be put
 *                into the environment; there is no requirement that
 *                the resulting string contains an equal sign
 * @param   args  Arguments for `fmt`
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(2) inline int
libexec_vputenvf_prepend(struct libexec_command *cmd, const char *fmt, va_list args)
{ return libexec_vputenvf(cmd, LIBEXEC_PREPEND, fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_putenvf(cmd, LIBEXEC_PREPEND, fmt, ...)`
 * 
 * @param   cmd  The command whose environment shall be modified
 * @param   fmt  Formatting string, following the rules of printf(3),
 *               for the environment variable that should be put
 *               into the environment; there is no requirement that
 *               the resulting string contains an equal sign
 * @param   ...  Arguments for `fmt`
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(2) inline int
libexec_putenvf_prepend(struct libexec_command *cmd, const char *fmt, ...)
{ LIBEXEC_VA_IMPL__(fmt, libexec_vputenvf_prepend, cmd, fmt); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenv(cmd, LIBEXEC_PREPEND, name, value)`
 * 
 * @param   cmd    The command whose environment shall be modified
 * @param   name   The name of the environment variable, must not
 *                 contain an equal sign
 * @param   value  The value that shall be associated with the
 *                 environment variable
 * @return         0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory to modify the command's environment
 */
inline int
libexec_setenv_prepend(struct libexec_command *cmd, const char *name, const char *value)
{ return libexec_setenv(cmd, LIBEXEC_PREPEND, name, value); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_vsetenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, args)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   args       Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_VPRINTF__(3) inline int
libexec_vsetenvf_prepend(struct libexec_command *cmd, const char *name, const char *value_fmt, va_list args)
{ return libexec_vsetenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, args); }

/**
 * Put an environment variable into a command's environment,
 * adding it to the beginning of the environment regardless
 * of whether the variable already exists
 * 
 * If the command's environment is in its default state (to
 * use the calling process's environment), the calling
 * process's environment will first be copied and set as the
 * command's environment
 * 
 * Equivalent to `libexec_setenvf(cmd, LIBEXEC_PREPEND, name, value_fmt, ...)`
 * 
 * @param   cmd        The command whose environment shall be modified
 * @param   name       The name of the environment variable, must not
 *                     contain an equal sign
 * @param   value_fmt  Formatting string, following the rules of printf(3),
 *                     for the environment variable's value
 * @param   ...        Arguments for `value_fmt`
 * @return             0 on success, -1 on failure
 * 
 * @throws  EINVAL     Invalid argument input
 * @throws  ENOMEM     Failed to allocate enough memory to modify the command's environment
 * @throws  EILSEQ     According to printf(3)
 * @throws  EOVERFLOW  According to printf(3)
 */
LIBEXEC_PRINTF__(3) inline int
libexec_setenvf_prepend(struct libexec_command *cmd, const char *name, const char *value_fmt, ...)
{ LIBEXEC_VA_IMPL__(value_fmt, libexec_vsetenvf_prepend, cmd, name, value_fmt); }


/**
 * Put a series of commands in a pipeline, piping each's
 * standard output to the next's (when there is one)
 * standard input
 * 
 * Pluming data added to the commands will prefix any
 * already existing pluming information
 * 
 * @param   how   Which type of file should be used to pipe
 *                together commands, and additional options.
 *                Must be one of the following values:
 *                - LIBEXEC_PIPE:
 *                    Use a normal pipe(7)
 *                - LIBEXEC_SOCK_STREAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_STREAM
 *                - LIBEXEC_SOCK_SEQPACKET
 *                    Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET
 *                - LIBEXEC_SOCK_DGRAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_DGRAM
 *                - LIBEXEC_DIRECT_PIPE
 *                    Use an O_DIRECT pipe(7) (see pipe2(2))
 *                Optionally, the file type may be OR'ed with
 *                LIBEXEC_PIPE_CIRCULARLY to pipe the last
 *                command's standard output to the first command's
 *                standard input.
 * @param   cmds  List of commands to put in a pipeline
 * @param   n     The number of elements in `cmds`
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory
 */
int libexec_pipe_commandsvn(enum libexec_pipe, struct libexec_command *const *, size_t);

/**
 * Put a series of commands in a pipeline, piping each's
 * standard output to the next's (when there is one)
 * standard input
 * 
 * Pluming data added to the commands will prefix any
 * already existing pluming information
 * 
 * @param   how   Which type of file should be used to pipe
 *                together commands, and additional options.
 *                Must be one of the following values:
 *                - LIBEXEC_PIPE:
 *                    Use a normal pipe(7)
 *                - LIBEXEC_SOCK_STREAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_STREAM
 *                - LIBEXEC_SOCK_SEQPACKET
 *                    Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET
 *                - LIBEXEC_SOCK_DGRAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_DGRAM
 *                - LIBEXEC_DIRECT_PIPE
 *                    Use an O_DIRECT pipe(7) (see pipe2(2))
 *                Optionally, the file type may be OR'ed with
 *                LIBEXEC_PIPE_CIRCULARLY to pipe the last
 *                command's standard output to the first command's
 *                standard input.
 * @param   cmds  `NULL` terminated list of commands to put in a pipeline
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory
 */
inline int
libexec_pipe_commandsv(enum libexec_pipe how, struct libexec_command *const *cmds)
{
	size_t n = 0;
	if (!cmds) {
		errno = EINVAL;
		return -1;
	}
	while (cmds[n])
		n++;
	return libexec_pipe_commandsvn(how, cmds, n);
}

/**
 * Put a series of commands in a pipeline, piping each's
 * standard output to the next's (when there is one)
 * standard input
 * 
 * Pluming data added to the commands will prefix any
 * already existing pluming information
 * 
 * @param   how   Which type of file should be used to pipe
 *                together commands, and additional options.
 *                Must be one of the following values:
 *                - LIBEXEC_PIPE:
 *                    Use a normal pipe(7)
 *                - LIBEXEC_SOCK_STREAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_STREAM
 *                - LIBEXEC_SOCK_SEQPACKET
 *                    Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET
 *                - LIBEXEC_SOCK_DGRAM
 *                    Use a unix(7) socketpair(3) with the type SOCK_DGRAM
 *                - LIBEXEC_DIRECT_PIPE
 *                    Use an O_DIRECT pipe(7) (see pipe2(2))
 *                Optionally, the file type may be OR'ed with
 *                LIBEXEC_PIPE_CIRCULARLY to pipe the last
 *                command's standard output to the first command's
 *                standard input.
 * @param   args  `NULL` terminated list of commands to put in a pipeline
 * @return        0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory
 */
int libexec_vpipe_commands(enum libexec_pipe, va_list);

/**
 * Put a series of commands in a pipeline, piping each's
 * standard output to the next's (when there is one)
 * standard input
 * 
 * Pluming data added to the commands will prefix any
 * already existing pluming information
 * 
 * @param   how  Which type of file should be used to pipe
 *               together commands, and additional options.
 *               Must be one of the following values:
 *               - LIBEXEC_PIPE:
 *                   Use a normal pipe(7)
 *               - LIBEXEC_SOCK_STREAM
 *                   Use a unix(7) socketpair(3) with the type SOCK_STREAM
 *               - LIBEXEC_SOCK_SEQPACKET
 *                   Use a unix(7) socketpair(3) with the type SOCK_SEQPACKET
 *               - LIBEXEC_SOCK_DGRAM
 *                   Use a unix(7) socketpair(3) with the type SOCK_DGRAM
 *               - LIBEXEC_DIRECT_PIPE
 *                   Use an O_DIRECT pipe(7) (see pipe2(2))
 *               Optionally, the file type may be OR'ed with
 *               LIBEXEC_PIPE_CIRCULARLY to pipe the last
 *               command's standard output to the first command's
 *               standard input.
 * @param   ...  `NULL` terminated list of commands to put in a pipeline
 * @return       0 on success, -1 on failure
 * 
 * @throws  EINVAL  Invalid argument input
 * @throws  ENOMEM  Failed to allocate enough memory
 */
inline int
libexec_pipe_commands(enum libexec_pipe how, ...)
{ LIBEXEC_VA_IMPL__(how, libexec_vpipe_commands, how); }


int libexec_get_documents(struct libexec_command *cmd, struct libexec_document **docsp, size_t *ndocsp, int flags);
int libexec_exec(struct libexec_command *cmd);
int libexec_spawn(pid_t *out, struct libexec_command *cmd,
                  int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user),
                  void *user, struct libexec_document **docsp, size_t *ndocsp, int doc_fd_flags);
int libexec_send_document(struct libexec_document *doc);
int libexec_recv_document(struct libexec_document *doc);

/* TODO maybe its better to have `struct libexec_run_how` */
int libexec_run_pipeline(int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user1), int alien_epoll, void *user1,
                         int (*on_alien_child_death)(pid_t pid, void *user2), void *user2,
                         int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user3), void *user3,
                         int (*reap_mutex_control)(int action /* -1 = acquire, +1 = release */, void *user4), void *user4,
                         int (*on_interrupt)(void *user5), void *user5,
                         const sigset_t *sigmask /* nullable */,
                         struct libexec_document *const *docs, size_t ndocs, int docs_1_level,
                         struct libexec_command *const *cmds, int *exit_statuses_out /* nullable */, size_t ncmds);
#define LIBEXEC_RUN_PIPELINE_NO_EPOLL NULL, -1, NULL
#define LIBEXEC_RUN_PIPELINE_NO_CHILDREN NULL, NULL
#define LIBEXEC_RUN_PIPELINE_NO_AFTER_FORK NULL, NULL
#define LIBEXEC_RUN_PIPELINE_NO_THREADING NULL, NULL
#define LIBEXEC_RUN_PIPELINE_NO_INTERRUPT NULL, NULL
#define LIBEXEC_RUN_PIPELINE_NO_SIGMASK NULL
#define LIBEXEC_RUN_PIPELINE_NO_OUTPUT NULL, 0, 0
#define LIBEXEC_RUN_PIPELINE_ONLY_OUTPUT\
	LIBEXEC_RUN_PIPELINE_NO_EPOLL,\
	LIBEXEC_RUN_PIPELINE_NO_CHILDREN,\
	LIBEXEC_RUN_PIPELINE_NO_AFTER_FORK,\
	LIBEXEC_RUN_PIPELINE_NO_THREADING,\
	LIBEXEC_RUN_PIPELINE_NO_INTERRUPT
#define LIBEXEC_RUN_PIPELINE_NOTHING\
	LIBEXEC_RUN_PIPELINE_ONLY_OUTPUT,\
	LIBEXEC_RUN_PIPELINE_NO_OUTPUT

int libexec_vrun(struct libexec_result *out, va_list args);
inline int libexec_run(struct libexec_result *out, ...)
{ LIBEXEC_VA_IMPL__(out, libexec_vrun, out); }


#endif