diff options
94 files changed, 10442 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..942ebc5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*\#* +*~ +*.o +*.a +*.lo +*.to +*.su +*.so +*.so.* +*.dll +*.dylib +*.gch +*.gcov +*.gcno +*.gcda +*.test @@ -0,0 +1,15 @@ +ISC License + +© 2024 Mattias Andrée <maandree@kth.se> + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d159bef --- /dev/null +++ b/Makefile @@ -0,0 +1,177 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OS = linux +# Linux: linux +# Mac OS: macos +# Windows: windows +include mk/$(OS).mk + + +LIB_MAJOR = 1 +LIB_MINOR = 0 +LIB_VERSION = $(LIB_MAJOR).$(LIB_MINOR) +LIB_NAME = exec + + +OBJ =\ + libexec_add_pluming.o\ + libexec_add_output_document.o\ + libexec_add_output_fd.o\ + libexec_clear_environ.o\ + libexec_close.o\ + libexec_construct_command.o\ + libexec_copy_environ.o\ + libexec_destroy_command.o\ + libexec_destroy_document.o\ + libexec_destroy_pluming.o\ + libexec_destroy_result.o\ + libexec_dup.o\ + libexec_exec.o\ + libexec_get_documents.o\ + libexec_getenv.o\ + libexec_getenv_last.o\ + libexec_init_command.o\ + libexec_input_data_copy.o\ + libexec_input_data_gift.o\ + libexec_input_text_copy.o\ + libexec_input_text_gift.o\ + libexec_open.o\ + libexec_openat.o\ + libexec_openat2.o\ + libexec_pipe_commands.o\ + libexec_pipe_commandsv.o\ + libexec_pipe_commandsvn.o\ + libexec_put_arguments.o\ + libexec_put_argumentsn.o\ + libexec_putenv.o\ + libexec_putenv_append.o\ + libexec_putenv_noclobber.o\ + libexec_putenv_noreplace.o\ + libexec_putenv_prepend.o\ + libexec_putenv_replace.o\ + libexec_putenvf.o\ + libexec_putenvf_append.o\ + libexec_putenvf_noclobber.o\ + libexec_putenvf_noreplace.o\ + libexec_putenvf_prepend.o\ + libexec_putenvf_replace.o\ + libexec_recv_document.o\ + libexec_renumber_fd.o\ + libexec_run.o\ + libexec_run_pipeline.o\ + libexec_send_document.o\ + libexec_set_environ.o\ + libexec_set_exec_fd.o\ + libexec_set_exec_path.o\ + libexec_set_executable.o\ + libexec_set_require_path.o\ + libexec_setenv.o\ + libexec_setenv_append.o\ + libexec_setenv_noclobber.o\ + libexec_setenv_noreplace.o\ + libexec_setenv_prepend.o\ + libexec_setenv_replace.o\ + libexec_setenvf.o\ + libexec_setenvf_append.o\ + libexec_setenvf_noclobber.o\ + libexec_setenvf_noreplace.o\ + libexec_setenvf_prepend.o\ + libexec_setenvf_replace.o\ + libexec_spawn.o\ + libexec_unsetenv.o\ + libexec_unsetenv_first.o\ + libexec_unsetenv_last.o\ + libexec_vconstruct_command.o\ + libexec_vpipe_commands.o\ + libexec_vputenvf.o\ + libexec_vputenvf_append.o\ + libexec_vputenvf_noclobber.o\ + libexec_vputenvf_noreplace.o\ + libexec_vputenvf_prepend.o\ + libexec_vputenvf_replace.o\ + libexec_vrun.o\ + libexec_vsetenvf.o\ + libexec_vsetenvf_append.o\ + libexec_vsetenvf_noclobber.o\ + libexec_vsetenvf_noreplace.o\ + libexec_vsetenvf_prepend.o\ + libexec_vsetenvf_replace.o + +HDR =\ + libexec.h\ + common.h + +LOBJ = $(OBJ:.o=.lo) +TOBJ = $(OBJ:.o=.to) +TEST = $(OBJ:.o=.test) + + +all: libexec.a libexec.$(LIBEXT) $(TEST) +$(OBJ): $(HDR) +$(LOBJ): $(HDR) +$(TOBJ): $(HDR) +$(TEST): testhelp.o libexec.a + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.lo: + $(CC) -fPIC -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +.c.to: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) -DTEST + +.to.test: + $(CC) -o $@ $< testhelp.o libexec.a $(LDFLAGS) + +libexec.a: $(OBJ) + @rm -f -- $@ + $(AR) rc $@ $(OBJ) + $(AR) ts $@ > /dev/null + +libexec.$(LIBEXT): $(LOBJ) + $(CC) $(LIBFLAGS) -o $@ $(LOBJ) $(LDFLAGS) + +check: $(TEST) + @status=0;\ + for t in $(TEST); do\ + if test -z "$(CHECK_PREFIX)"; then\ + printf './%s\n' "$$t";\ + else\ + printf '%s ./%s\n' "$(CHECK_PREFIX)" "$$t";\ + fi;\ + if ! $(CHECK_PREFIX) "./$$t"; then\ + printf '\033[1;31m%s failed!\033[m\n' "$$t";\ + status=1;\ + fi;\ + done;\ + exit $$status + +install: libexec.a libexec.$(LIBEXT) + mkdir -p -- "$(DESTDIR)$(PREFIX)/lib" + mkdir -p -- "$(DESTDIR)$(PREFIX)/include" + cp -- libexec.a "$(DESTDIR)$(PREFIX)/lib/" + cp -- libexec.$(LIBEXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + $(FIX_INSTALL_NAME) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + ln -sf -- libexec.$(LIBMINOREXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" + ln -sf -- libexec.$(LIBMAJOREXT) "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBEXT)" + cp -- libexec.h "$(DESTDIR)$(PREFIX)/include/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.a" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBMINOREXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/lib/libexec.$(LIBEXT)" + -rm -f -- "$(DESTDIR)$(PREFIX)/include/libexec.h" + +clean: + -rm -f -- *.o *.a *.lo *.to *.su *.so *.so.* *.dll *.dylib + -rm -f -- *.gch *.gcov *.gcno *.gcda *.test *.$(LIBEXT) + +.SUFFIXES: +.SUFFIXES: .lo .o .c .to .test + +.PHONY: all check install uninstall clean @@ -0,0 +1,14 @@ +NAME + libexec - Library for running complex external utilities + +DESCRIPTION + libexec is a library that is designed to make it easy + to safely and correctly run external utilities, including + creating pipe lines. + + libexec is both designed to aid shell implementations and + to make it easier to write programs run a lot of commands + but could not easily be written as a shell script. + +SEE ALSO + None. @@ -0,0 +1,13 @@ +Test that LIBEXEC_NOREPLACE first copies the calling process's environment, +even if nothing is modified, but also test that LIBEXEC_NOCLOBBER does not +do this. + +add LIBEXEC_RUN_ADD_SHARED_OUTPUT_FD +add LIBEXEC_RUN_ADD_SHARED_OUTPUT +add LIBEXEC_RUN_GET_STDOUT +add LIBEXEC_RUN_GET_STDERR +add LIBEXEC_RUN_SET_USER_TAG +add a way to create process substitutions in libexec_run +add a way to add a fork of the process into a pipeline + +add man pages diff --git a/common.h b/common.h new file mode 100644 index 0000000..66a8ded --- /dev/null +++ b/common.h @@ -0,0 +1,271 @@ +/* See LICENSE file for copyright and license details. */ +#include "libexec.h" + +#include <sys/epoll.h> +#include <sys/socket.h> +#include <sys/syscall.h> +#include <sys/wait.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if !defined(__linux__) +int libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, void *how, size_t size); +#endif + + +#ifdef TEST +# define TESTED_ELSEWHERE LIBEXEC_CONST__ int main(void) { return 0; } + +enum assert_type { + ASSERT_ENUM, + ASSERT_UINT, + ASSERT_INT, + ASSERT_NULL, + ASSERT_PTR, + ASSERT_STR, + ASSERT_STR_REV +}; + +enum assert_how { + ASSERT_EQ, + ASSERT_NE, + ASSERT_LT, + ASSERT_LE, + ASSERT_GT, + ASSERT_GE, + ASSERT_CONTAINS_ALL, + ASSERT_CONTAINS_ANY, + ASSERT_CONTAINS_NOT_ALL, + ASSERT_CONTAINS_NOT_ANY, + ASSERT_IS_IN, + ASSERT_NOT_IN, + ASSERT_NOT_LT, + ASSERT_NOT_LE, + ASSERT_NOT_GT, + ASSERT_NOT_GE +}; + +void test_assert(const char *file, int line, enum assert_type type, enum assert_how how, + const char *have_string, const char *expect_string, const void *have, const void *expect); + +#define ASSERT_EQ_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_EQ, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_NE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LT_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_LT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GT_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_GT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_LE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GE_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_GE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_ALL_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_SOME_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NONE_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_NOT_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NOT_ALL_OF_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_IS_IN_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_IS_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NOT_IN_ENUM(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_ENUM, ASSERT_NOT_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_EQ_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_EQ, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_NE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_NE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LT_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_LT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GT_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_GT, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_LE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_LE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_GE_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_GE, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_ALL_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_SOME_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NONE_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_NOT_ANY, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_CONTAINS_NOT_ALL_OF_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_IS_IN_UINT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_UINT, ASSERT_IS_IN, #HAVE, #EXPECT,\ + &(uintmax_t){(uintmax_t)(HAVE)}, &(uintmax_t){(uintmax_t)(EXPECT)}) + +#define ASSERT_EQ_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_EQ, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_NE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_NE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_LT_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_LT, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_GT_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_GT, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_LE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_LE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_GE_INT(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_INT, ASSERT_GE, #HAVE, #EXPECT,\ + &(intmax_t){(intmax_t)(HAVE)}, &(intmax_t){(intmax_t)(EXPECT)}) + +#define ASSERT_EQ_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_EQ, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NE_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_STARTS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_EXTENDS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_TRUNCATES_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEGINNING_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_EXTENDS_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_TRUNCATES_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDING_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_STARTS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_EXTENDS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_TRUNCATES_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEGINNING_NOT_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_ENDS_WITH_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_EXTENDS_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_TRUNCATES_END_OF_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_ENDING_NOT_BOUNDED_TO_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR_REV, ASSERT_NOT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_CONTAINS_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_CONTAINS_ALL, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_DOES_NOT_CONTAIN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_CONTAINS_NOT_ALL, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_IN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_IS_IN, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NOT_IN_STR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_STR, ASSERT_NOT_IN, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_EQ, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_NOT_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_NE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEFORE_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_LT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_AFTER_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_GT, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_BEFORE_OR_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_LE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_AFTER_OR_IS_PTR(HAVE, EXPECT)\ + test_assert(__FILE__, __LINE__, ASSERT_PTR, ASSERT_GE, #HAVE, #EXPECT, (HAVE), (EXPECT)) + +#define ASSERT_IS_NULL(HAVE)\ + test_assert(__FILE__, __LINE__, ASSERT_NULL, ASSERT_EQ, #HAVE, "NULL", (HAVE), NULL) + +#define ASSERT_NOT_NULL(HAVE)\ + test_assert(__FILE__, __LINE__, ASSERT_NULL, ASSERT_NE, #HAVE, "NULL", (HAVE), NULL) + +#define ASSERT_IS_TRUE(HAVE)\ + ASSERT_NE_INT(HAVE, 0) + +#define ASSERT_IS_FALSE(HAVE)\ + ASSERT_EQ_INT(HAVE, 0) + +#define ASSERT_ZERO(HAVE)\ + ASSERT_EQ_INT(HAVE, 0) + + +#endif diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..f4adf12 --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = +LDFLAGS = diff --git a/libexec.h b/libexec.h new file mode 100644 index 0000000..5f7b2d0 --- /dev/null +++ b/libexec.h @@ -0,0 +1,1633 @@ +/* See LICENSE file for copyright and license details. */ +#ifndef LIBEXEC_H +#define LIBEXEC_H + + +#if defined(__linux__) +# include <linux/openat2.h> +#endif +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdint.h> + + +/* For internal use only { */ +#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_VERSION 0U + + +enum libexec_insert_mode { + LIBEXEC_REPLACE, /* replace existing definition if any */ + LIBEXEC_NOREPLACE, /* do nothing if definition already exists */ + LIBEXEC_NOCLOBBER, /* fail if definition already exists an differs */ + LIBEXEC_APPEND, /* add, to end of list, even if definition already exists, but also keep old */ + LIBEXEC_PREPEND /* add, to beginning of list, even if definition already exists, but also keep old */ + /* Either LIBEXEC_APPEND or LIBEXEC_PREPEND is required to put something missing '=' in the environment */ +#define LIBEXEC_INSERT_MODE__COUNT__ 5 +}; + +enum libexec_pluming_type { + LIBEXEC_PLUMING_OPENAT, + LIBEXEC_PLUMING_OPENAT2, + LIBEXEC_PLUMING_CLOSE, + LIBEXEC_PLUMING_DUP2, + LIBEXEC_PLUMING_DOCUMENT, + LIBEXEC_PLUMING_PIPE +#define LIBEXEC_PLUMING_TYPE__COUNT__ 6 +}; + +enum libexec_pipe { + LIBEXEC_PIPE = 0, + LIBEXEC_SOCK_STREAM = 1, + LIBEXEC_SOCK_SEQPACKET = 2, + LIBEXEC_SOCK_DGRAM = 3, + LIBEXEC_DIRECT_PIPE = 4, + LIBEXEC_PIPE_CIRCULARLY = 0x8000 +#define LIBEXEC_PIPE__COUNT__ 5 +}; + +enum libexec_locate_method { + LIBEXEC_ALLOW_NAME, + LIBEXEC_REQUIRE_PATH, + LIBEXEC_EXEC_FD, + LIBEXEC_EXEC_AT +#define LIBEXEC_LOCATE_METHOD__COUNT__ 4 +}; + +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 +}; +#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; +}; + +#define LIBEXEC_COMMAND_INIT\ + ((struct libexec_command){LIBEXEC_VERSION, LIBEXEC_ALLOW_NAME, -1, NULL, NULL, 0, NULL, 0, NULL}) +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; +}; + + +void libexec_init_command(struct libexec_command *cmd); +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); +int libexec_construct_command(struct libexec_command *cmd, const char *fmt, ...); +int libexec_put_argumentsn(struct libexec_command *cmd, const char *const *args, size_t nargs); +int libexec_put_arguments(struct libexec_command *cmd, const char *const *args); +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_open(struct libexec_command *cmd, int fd, const char *file, int flags, mode_t mode); +int libexec_openat(struct libexec_command *cmd, int fd, int dirfd, const char *file, int flags, mode_t 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); +int libexec_input_text_copy(struct libexec_command *cmd, int fd, const char *text); +int libexec_input_text_gift(struct libexec_command *cmd, int fd, char *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) int libexec_putenvf(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 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) int libexec_setenvf(struct libexec_command *, enum libexec_insert_mode, const char *, const char *, ...); + + +/** + * 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 + */ +int libexec_putenv_replace(struct libexec_command *, const char *); + +/** + * 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) int libexec_vputenvf_replace(struct libexec_command *, const char *, va_list); + +/** + * 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) int libexec_putenvf_replace(struct libexec_command *, const char *, ...); + +/** + * 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 + */ +int libexec_setenv_replace(struct libexec_command *, const char *, const char *); + +/** + * 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) int libexec_vsetenvf_replace(struct libexec_command *, const char *, const char *, va_list); + +/** + * 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) int libexec_setenvf_replace(struct libexec_command *, 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 + * + * 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 + */ +int libexec_putenv_noreplace(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 + * + * 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) int libexec_vputenvf_noreplace(struct libexec_command *, 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 + * + * 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) int libexec_putenvf_noreplace(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 + * + * 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 + */ +int libexec_setenv_noreplace(struct libexec_command *, 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 + * + * 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) int libexec_vsetenvf_noreplace(struct libexec_command *, 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 + * + * 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) int libexec_setenvf_noreplace(struct libexec_command *, 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 + * + * 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 + */ +int libexec_putenv_noclobber(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 + * + * 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) int libexec_vputenvf_noclobber(struct libexec_command *, 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 + * + * 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) int libexec_putenvf_noclobber(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 + * + * 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 + */ +int libexec_setenv_noclobber(struct libexec_command *, 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 + * + * 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) int libexec_vsetenvf_noclobber(struct libexec_command *, 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 + * + * 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) int libexec_setenvf_noclobber(struct libexec_command *, const char *, const char *, ...); + + +/** + * 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 + */ +int libexec_putenv_append(struct libexec_command *, const char *); + +/** + * 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) int libexec_vputenvf_append(struct libexec_command *, const char *, va_list); + +/** + * 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) int libexec_putenvf_append(struct libexec_command *, const char *, ...); + +/** + * 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 + */ +int libexec_setenv_append(struct libexec_command *, const char *, const char *); + +/** + * 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) int libexec_vsetenvf_append(struct libexec_command *, const char *, const char *, va_list); + +/** + * 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) int libexec_setenvf_append(struct libexec_command *, const char *, const char *, ...); + + +/** + * 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 + */ +int libexec_putenv_prepend(struct libexec_command *, const char *); + +/** + * 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) int libexec_vputenvf_prepend(struct libexec_command *, const char *, va_list); + +/** + * 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) int libexec_putenvf_prepend(struct libexec_command *, const char *, ...); + +/** + * 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 + */ +int libexec_setenv_prepend(struct libexec_command *, const char *, const char *); + +/** + * 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) int libexec_vsetenvf_prepend(struct libexec_command *, const char *, const char *, va_list); + +/** + * 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) int libexec_setenvf_prepend(struct libexec_command *, const char *, const char *, ...); + + +/** + * 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 how, struct libexec_command *const *cmds, size_t 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 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 + */ +int libexec_pipe_commandsv(enum libexec_pipe how, struct libexec_command *const *cmds); + +/** + * 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 how, va_list args); + +/** + * 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 + */ +int libexec_pipe_commands(enum libexec_pipe 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); +int libexec_run(struct libexec_result *out, ...); + + +#endif diff --git a/libexec_add_output_document.c b/libexec_add_output_document.c new file mode 100644 index 0000000..9eb1f8d --- /dev/null +++ b/libexec_add_output_document.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_output_document(struct libexec_command *cmd, int fd, struct libexec_document *doc, int flags) +{ + int fd_flags, fl_flags, fds[2]; + + if (!cmd || fd < 0 || !doc) { + errno = EINVAL; + return -1; + } + + fd_flags = (flags & O_CLOEXEC); + fl_flags = (flags ^ fd_flags); + if (fd_flags) + fd_flags = FD_CLOEXEC; + + if (pipe(fds)) + return -1; + if (fd_flags && fcntl(fds[0], F_SETFD, fd_flags)) + goto fail; + if (fl_flags && fcntl(fds[0], F_SETFL, fl_flags)) + goto fail; + + doc->fd = fds[0]; + + if (libexec_dup(cmd, fd, fds[1])) + goto fail; + cmd->plumings[cmd->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + return 0; + +fail: + close(fds[0]); + close(fds[1]); + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_add_output_fd.c b/libexec_add_output_fd.c new file mode 100644 index 0000000..6eae580 --- /dev/null +++ b/libexec_add_output_fd.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_output_fd(struct libexec_command *cmd, int fd, int wr_fd) +{ + return libexec_renumber_fd(cmd, fd, wr_fd); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_add_pluming.c b/libexec_add_pluming.c new file mode 100644 index 0000000..108e8e8 --- /dev/null +++ b/libexec_add_pluming.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_add_pluming(struct libexec_command *cmd, const struct libexec_pluming *pluming) +{ + void *new; + if (!cmd || !pluming) { + errno = EINVAL; + return -1; + } + new = realloc(cmd->plumings, (cmd->nplumings + 1) * sizeof(*cmd->plumings)); + if (!new) + return -1; + cmd->plumings = new; + memcpy(&cmd->plumings[cmd->nplumings++], pluming, sizeof(*pluming)); + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_clear_environ.c b/libexec_clear_environ.c new file mode 100644 index 0000000..9f09561 --- /dev/null +++ b/libexec_clear_environ.c @@ -0,0 +1,72 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_clear_environ(struct libexec_command *cmd) +{ + size_t i; + char **new_env; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + new_env = calloc(1, sizeof(*cmd->environ)); + if (!new_env) + return -1; + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = new_env; + return 0; +} + + +#else + + +static void +check_clear_from_default(void) +{ + struct libexec_command cmd, ref; + char **old_env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + ASSERT_IS_NULL(cmd.environ); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + old_env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = old_env; + + libexec_destroy_command(&cmd); +} + + +int +main(void) +{ + errno = 0; + ASSERT_EQ_INT(libexec_clear_environ(NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + + check_clear_from_default(); + /* XXX test malloc failure */ + /* XXX test that function deallocates */ + return 0; +} + + +#endif diff --git a/libexec_close.c b/libexec_close.c new file mode 100644 index 0000000..b1af338 --- /dev/null +++ b/libexec_close.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_close(struct libexec_command *cmd, int fd) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_CLOSE; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_construct_command.c b/libexec_construct_command.c new file mode 100644 index 0000000..36b18f9 --- /dev/null +++ b/libexec_construct_command.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_construct_command(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vconstruct_command(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_copy_environ.c b/libexec_copy_environ.c new file mode 100644 index 0000000..2c7e19f --- /dev/null +++ b/libexec_copy_environ.c @@ -0,0 +1,51 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_copy_environ(struct libexec_command *cmd, const char *const *env) +{ + size_t i, n; + char **new_env; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + if (!env) + env = *(const char *const **)(void *)&environ; + + n = 0; + while (env[n]) + n++; + + new_env = calloc((n + 1), sizeof(*cmd->environ)); + if (!new_env) + return -1; + + for (i = 0; i < n; i++) { + new_env[i] = strdup(env[i]); + if (!new_env[i]) { + while (i--) + free(new_env[i]); + free(new_env); + return -1; + } + } + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = new_env; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_command.c b/libexec_destroy_command.c new file mode 100644 index 0000000..d5382fb --- /dev/null +++ b/libexec_destroy_command.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_command(struct libexec_command *cmd) +{ + size_t i; + + if (!cmd) + return; + + free(cmd->executable); + + for (i = 0; i < cmd->narguments; i++) + free(cmd->arguments[i]); + free(cmd->arguments); + + for (i = 0; i < cmd->nplumings; i++) + libexec_destroy_pluming(&cmd->plumings[i]); + free(cmd->plumings); + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + *cmd = LIBEXEC_COMMAND_INIT; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_document.c b/libexec_destroy_document.c new file mode 100644 index 0000000..aee6298 --- /dev/null +++ b/libexec_destroy_document.c @@ -0,0 +1,28 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_destroy_document(struct libexec_document *doc) +{ + int ret = 0; + + if (!doc) + return 0; + + free(doc->text); + doc->text = NULL; + doc->length = 0; + doc->offset = 0; + if (doc->fd >= 0) { + ret = close(doc->fd); + doc->fd = -1; + } + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_pluming.c b/libexec_destroy_pluming.c new file mode 100644 index 0000000..6aae3c7 --- /dev/null +++ b/libexec_destroy_pluming.c @@ -0,0 +1,31 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_pluming(struct libexec_pluming *pluming) +{ + if (!pluming) + return; + + if (pluming->type == LIBEXEC_PLUMING_OPENAT || + pluming->type == LIBEXEC_PLUMING_OPENAT2) { + free(pluming->target.file); + pluming->target.file = NULL; + + } else if (pluming->type == LIBEXEC_PLUMING_DOCUMENT) { + free(pluming->target.text); + pluming->target.text = NULL; + + } else if (pluming->type == LIBEXEC_PLUMING_PIPE) { + if (pluming->target.fd >= 0) + close(pluming->target.fd); + pluming->target.fd = -1; + } +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_destroy_result.c b/libexec_destroy_result.c new file mode 100644 index 0000000..0587c3b --- /dev/null +++ b/libexec_destroy_result.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_destroy_result(struct libexec_result *result) +{ + if (!result) + return; + + free(result->exit_statuses); + result->exit_statuses = NULL; + result->proc_count = 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_dup.c b/libexec_dup.c new file mode 100644 index 0000000..674acb7 --- /dev/null +++ b/libexec_dup.c @@ -0,0 +1,24 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_dup(struct libexec_command *cmd, int fd, int old_fd) +{ + struct libexec_pluming pluming; + if (fd < 0 || old_fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_DUP2; + pluming.target.fd = old_fd; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_exec.c b/libexec_exec.c new file mode 100644 index 0000000..3f306d0 --- /dev/null +++ b/libexec_exec.c @@ -0,0 +1,200 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +static char * +which(const char *name, char *path, char **end_colon_out) +{ + size_t namelen_nul, dirlen, maxlen; + char *p, *q, *buf; + int eacces_encountered = 0; + + if (!path || !*path) + goto enoent; + + maxlen = 0; + for (p = path; p; p = q) { + q = strchr(p, ':'); + if (q) + dirlen = (size_t)(q++ - p); + else + dirlen = strlen(p); + if (dirlen > maxlen) + dirlen = maxlen; + } + + namelen_nul = strlen(name) + 1; + + buf = NULL; + if (maxlen) { + buf = malloc(maxlen + 1 + namelen_nul); + if (!buf) + return NULL; + } + + for (; path; path = q) { + q = strchr(p, ':'); + if (q) + dirlen = (size_t)(q++ - path); + else + dirlen = strlen(path); + p = buf; + if (dirlen) { + memcpy(p, path, dirlen); + p = &p[dirlen]; + *p++ = '/'; + } + memcpy(p, name, namelen_nul); + if (!faccessat(AT_FDCWD, buf, X_OK, AT_EACCESS)) { + *end_colon_out = q; + return buf; + } + if (errno == EACCES) + eacces_encountered = 1; + } + + free(buf); + + if (eacces_encountered) { + errno = EACCES; + return NULL; + } + +enoent: + errno = ENOENT; + return NULL; +} + + +static void +exec_path(struct libexec_command *cmd) +{ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ); +} + + +static void +exec_name(struct libexec_command *cmd) +{ + /* execvpe is non-standard */ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + char *end_colon = NULL, *file_free = NULL; + if (!strchr(file, '/')) { + file = file_free = which(file, getenv("PATH"), &end_colon); + if (!file) + return; + } + execve(file, cmd->arguments, cmd->environ ? cmd->environ : environ); + free(file_free); + if (end_colon) + *end_colon = ':'; +} + + +static void +exec_fd(struct libexec_command *cmd) +{ + fexecve(cmd->exec_fd, cmd->arguments, cmd->environ ? cmd->environ : environ); +} + + +static void +exec_at(struct libexec_command *cmd) +{ + const char *file = cmd->executable ? cmd->executable : cmd->arguments[0]; + execveat(cmd->exec_fd, file, cmd->arguments, cmd->environ ? cmd->environ : environ, 0); +} + + +int +libexec_exec(struct libexec_command *cmd) +{ + void (*exec_function)(struct libexec_command *cmd); + size_t i; + int fd; + + if (!cmd) + goto einval; + + if (cmd->library_version > LIBEXEC_VERSION) + abort(); + + if (!cmd->narguments) + goto einval; + + if (cmd->exec_how == LIBEXEC_REQUIRE_PATH) + exec_function = &exec_path; + else if (cmd->exec_how == LIBEXEC_ALLOW_NAME) + exec_function = &exec_name; + else if (cmd->exec_how == LIBEXEC_EXEC_FD) + exec_function = &exec_fd; + else if (cmd->exec_how == LIBEXEC_EXEC_AT) + exec_function = &exec_at; + else + goto einval; + + for (i = 0; i < cmd->nplumings; i++) { + switch (cmd->plumings[i].type) { + case LIBEXEC_PLUMING_CLOSE: + if (cmd->plumings[i].fd >= 0) { + close(cmd->plumings[i].fd); + cmd->plumings[i].fd = -1; + } + break; + +#if defined(__linux__) + case LIBEXEC_PLUMING_OPENAT2: + fd = (int)syscall(SYS_openat2, + cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file, + cmd->plumings[i].target.how, cmd->plumings[i].target.how_size); + if (fd < 0) + return -1; + free(cmd->plumings[i].target.file); + free(cmd->plumings[i].target.how); + goto openned_fd; +#endif + + case LIBEXEC_PLUMING_OPENAT: + fd = openat(cmd->plumings[i].target.dirfd, cmd->plumings[i].target.file, + cmd->plumings[i].target.flags, cmd->plumings[i].target.mode); + if (fd < 0) + return -1; + free(cmd->plumings[i].target.file); +#if defined(__linux__) + openned_fd: +#endif + cmd->plumings[i].type = LIBEXEC_PLUMING_PIPE; + cmd->plumings[i].target.fd = fd; + /* fall through */ + + case LIBEXEC_PLUMING_DUP2: + case LIBEXEC_PLUMING_PIPE: + if (cmd->plumings[i].fd == cmd->plumings[i].target.fd) + break; + if (dup2(cmd->plumings[i].target.fd, cmd->plumings[i].fd) == -1) + return -1; + if (cmd->plumings[i].type == LIBEXEC_PLUMING_PIPE) + close(cmd->plumings[i].target.fd); + cmd->plumings[i].target.fd = cmd->plumings[i].fd; + break; + + default: + case LIBEXEC_PLUMING_DOCUMENT: + goto einval; + } + } + + (*exec_function)(cmd); + return -1; + +einval: + errno = EINVAL; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_get_documents.c b/libexec_get_documents.c new file mode 100644 index 0000000..2bdadf8 --- /dev/null +++ b/libexec_get_documents.c @@ -0,0 +1,83 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_get_documents(struct libexec_command *cmd, struct libexec_document **docsp, size_t *ndocsp, int flags) +{ + typedef int fdpair[2]; + size_t i, j, n; + void *new; + fdpair *pipes; + int error; + int fd_flags, fl_flags; + + if (!cmd || !docsp || !ndocsp) { + errno = EINVAL; + return -1; + } + + fd_flags = (flags & O_CLOEXEC); + fl_flags = (flags ^ fd_flags); + if (fd_flags) + fd_flags = FD_CLOEXEC; + + n = 0; + for (i = 0; i < cmd->nplumings; i++) + if (cmd->plumings[i].type == LIBEXEC_PLUMING_DOCUMENT) + n += 1; + + pipes = malloc(n * sizeof(*pipes)); + if (!pipes) + return -1; + + j = 0; + for (i = 0; i < cmd->nplumings; i++) { + if (cmd->plumings[i].type == LIBEXEC_PLUMING_DOCUMENT) { + if (pipe(pipes[j])) + goto fail; + j++; + if (fd_flags && fcntl(pipes[j - 1][1], F_SETFD, fd_flags)) + goto fail; + if (fl_flags && fcntl(pipes[j - 1][1], F_SETFL, fl_flags)) + goto fail; + } + } + + new = realloc(*docsp, (*ndocsp + n) * sizeof(**docsp)); + if (!new) + goto fail; + *docsp = new; + + for (i = 0, j = 0; i < cmd->nplumings; i++) { + if (cmd->plumings[i].type != LIBEXEC_PLUMING_DOCUMENT) + continue; + (*docsp)[j].user = 0; + (*docsp)[j].fd = pipes[j][1]; + (*docsp)[j].text = cmd->plumings[i].target.text; + (*docsp)[j].length = cmd->plumings[i].target.len; + (*docsp)[j].offset = 0; + cmd->plumings[i].target.fd = pipes[j][0]; + cmd->plumings[i].type = LIBEXEC_PLUMING_PIPE; + j++; + } + + free(pipes); + return 0; + +fail: + error = errno; + while (j--) { + close(pipes[j][0]); + close(pipes[j][1]); + } + free(pipes); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_getenv.c b/libexec_getenv.c new file mode 100644 index 0000000..5099b28 --- /dev/null +++ b/libexec_getenv.c @@ -0,0 +1,122 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const char * +libexec_getenv(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, len; + + if (!cmd || !name) { + errno = EINVAL; + return NULL; + } + + if (strchr(name, '=')) + return NULL; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + return &env[i][len + 1]; + + return NULL; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + env = environ; + environ = calloc(12, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("AA=aa"); + environ[1] = strdup("A=a0"); + environ[2] = strdup("B=b0"); + environ[3] = strdup("A=a1"); + environ[4] = strdup("B=b1"); + environ[5] = strdup("A=a2"); + environ[6] = strdup("B=b2"); + environ[7] = strdup("A=a3"); + environ[8] = strdup("B=b3"); + environ[9] = strdup("BB=bb"); + environ[10] = strdup("X=Y=Z"); + ASSERT_NOT_NULL(environ[0]); + ASSERT_NOT_NULL(environ[1]); + ASSERT_NOT_NULL(environ[2]); + ASSERT_NOT_NULL(environ[3]); + ASSERT_NOT_NULL(environ[4]); + ASSERT_NOT_NULL(environ[5]); + ASSERT_NOT_NULL(environ[6]); + ASSERT_NOT_NULL(environ[7]); + ASSERT_NOT_NULL(environ[8]); + ASSERT_NOT_NULL(environ[9]); + ASSERT_NOT_NULL(environ[10]); + ASSERT_IS_NULL(environ[11]); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "PATH")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(NULL, "x")); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "x")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, NULL)); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "X=Y")); + ASSERT_IS_TRUE(errno == 0 || errno == EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv(&cmd, "")); + ASSERT_IS_TRUE(errno == 0); + + ASSERT_EQ_STR(libexec_getenv(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "A"), "a0"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "B"), "b0"); + + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + libexec_destroy_command(&cmd); + + ASSERT_IS_NULL(libexec_getenv(&cmd, "PATH")); + ASSERT_IS_NULL(libexec_getenv(NULL, "x")); + ASSERT_IS_NULL(libexec_getenv(&cmd, "x")); + ASSERT_IS_NULL(libexec_getenv(&cmd, NULL)); + ASSERT_IS_NULL(libexec_getenv(&cmd, "X=Y")); + ASSERT_IS_NULL(libexec_getenv(&cmd, "")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "A"), "a0"); + ASSERT_EQ_STR(libexec_getenv(&cmd, "B"), "b0"); + + libexec_set_environ(&cmd, environ); + environ = env; + + return 0; +} + + +#endif diff --git a/libexec_getenv_last.c b/libexec_getenv_last.c new file mode 100644 index 0000000..1de318e --- /dev/null +++ b/libexec_getenv_last.c @@ -0,0 +1,123 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +const char * +libexec_getenv_last(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, len; + const char *ret = NULL; + + if (!cmd || !name) { + errno = EINVAL; + return NULL; + } + + if (strchr(name, '=')) + return NULL; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + ret = &env[i][len + 1]; + + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + env = environ; + environ = calloc(12, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("AA=aa"); + environ[1] = strdup("A=a0"); + environ[2] = strdup("B=b0"); + environ[3] = strdup("A=a1"); + environ[4] = strdup("B=b1"); + environ[5] = strdup("A=a2"); + environ[6] = strdup("B=b2"); + environ[7] = strdup("A=a3"); + environ[8] = strdup("B=b3"); + environ[9] = strdup("BB=bb"); + environ[10] = strdup("X=Y=Z"); + ASSERT_NOT_NULL(environ[0]); + ASSERT_NOT_NULL(environ[1]); + ASSERT_NOT_NULL(environ[2]); + ASSERT_NOT_NULL(environ[3]); + ASSERT_NOT_NULL(environ[4]); + ASSERT_NOT_NULL(environ[5]); + ASSERT_NOT_NULL(environ[6]); + ASSERT_NOT_NULL(environ[7]); + ASSERT_NOT_NULL(environ[8]); + ASSERT_NOT_NULL(environ[9]); + ASSERT_NOT_NULL(environ[10]); + ASSERT_IS_NULL(environ[11]); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "PATH")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(NULL, "x")); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "x")); + ASSERT_IS_TRUE(errno == 0); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, NULL)); + ASSERT_EQ_INT(errno, EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "X=Y")); + ASSERT_IS_TRUE(errno == 0 || errno == EINVAL); + + errno = 0; + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "")); + ASSERT_IS_TRUE(errno == 0); + + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "A"), "a3"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "B"), "b3"); + + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + libexec_destroy_command(&cmd); + + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "PATH")); + ASSERT_IS_NULL(libexec_getenv_last(NULL, "x")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "x")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, NULL)); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "X=Y")); + ASSERT_IS_NULL(libexec_getenv_last(&cmd, "")); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "X"), "Y=Z"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "AA"), "aa"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "BB"), "bb"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "A"), "a3"); + ASSERT_EQ_STR(libexec_getenv_last(&cmd, "B"), "b3"); + + libexec_set_environ(&cmd, environ); + environ = env; + + return 0; +} + + +#endif diff --git a/libexec_init_command.c b/libexec_init_command.c new file mode 100644 index 0000000..c49af27 --- /dev/null +++ b/libexec_init_command.c @@ -0,0 +1,43 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_init_command(struct libexec_command *cmd) +{ + if (!cmd) + return; + + *cmd = LIBEXEC_COMMAND_INIT; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd; + + memset(&cmd, 127, sizeof(cmd)); + libexec_init_command(&cmd); + + ASSERT_EQ_INT(cmd.library_version, LIBEXEC_VERSION); + ASSERT_EQ_ENUM(cmd.exec_how, LIBEXEC_ALLOW_NAME); + ASSERT_EQ_INT(cmd.exec_fd, -1); + ASSERT_IS_NULL(cmd.executable); + ASSERT_IS_NULL(cmd.arguments); + ASSERT_EQ_UINT(cmd.narguments, 0); + ASSERT_IS_NULL(cmd.plumings); + ASSERT_EQ_UINT(cmd.nplumings, 0); + ASSERT_IS_NULL(cmd.environ); + + libexec_init_command(NULL); + + return 0; +} + + +#endif diff --git a/libexec_input_data_copy.c b/libexec_input_data_copy.c new file mode 100644 index 0000000..bb401e0 --- /dev/null +++ b/libexec_input_data_copy.c @@ -0,0 +1,23 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_data_copy(struct libexec_command *cmd, int fd, const char *data, size_t len) +{ + char *copy = malloc(len); + if (!copy) + return -1; + memcpy(copy, data, len); + if (libexec_input_data_gift(cmd, fd, copy, len)) { + free(copy); + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_data_gift.c b/libexec_input_data_gift.c new file mode 100644 index 0000000..77ee80f --- /dev/null +++ b/libexec_input_data_gift.c @@ -0,0 +1,25 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_input_data_gift(struct libexec_command *cmd, int fd, char *data, size_t len) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_DOCUMENT; + pluming.target.text = data; + pluming.target.len = len; + return libexec_add_pluming(cmd, &pluming); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_text_copy.c b/libexec_input_text_copy.c new file mode 100644 index 0000000..da12bfd --- /dev/null +++ b/libexec_input_text_copy.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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)); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_input_text_gift.c b/libexec_input_text_gift.c new file mode 100644 index 0000000..94b09c3 --- /dev/null +++ b/libexec_input_text_gift.c @@ -0,0 +1,19 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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)); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_open.c b/libexec_open.c new file mode 100644 index 0000000..7aa2698 --- /dev/null +++ b/libexec_open.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_openat.c b/libexec_openat.c new file mode 100644 index 0000000..41498ba --- /dev/null +++ b/libexec_openat.c @@ -0,0 +1,36 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_openat(struct libexec_command *cmd, int fd, int dirfd, const char *file, int flags, mode_t mode) +{ + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_OPENAT; + pluming.target.dirfd = dirfd; + pluming.target.flags = flags; + pluming.target.mode = mode; + pluming.target.file = NULL; + if (file) { + pluming.target.file = strdup(file); + if (!pluming.target.file) + return -1; + } + if (libexec_add_pluming(cmd, &pluming)) { + free(pluming.target.file); + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_openat2.c b/libexec_openat2.c new file mode 100644 index 0000000..ff97bff --- /dev/null +++ b/libexec_openat2.c @@ -0,0 +1,54 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_openat2(struct libexec_command *cmd, int fd, int dirfd, const char *file, struct open_how *how, size_t size) +{ +#if defined(__linux__) + + struct libexec_pluming pluming; + if (fd < 0) { + errno = EINVAL; + return -1; + } + memset(&pluming, 0, sizeof(pluming)); + pluming.fd = fd; + pluming.type = LIBEXEC_PLUMING_OPENAT2; + pluming.target.dirfd = dirfd; + pluming.target.how = NULL; + pluming.target.file = NULL; + if (file) { + pluming.target.file = strdup(file); + if (!pluming.target.file) + return -1; + } + if (size) { + pluming.target.how = malloc(size); + if (!pluming.target.how) { + free(pluming.target.file); + return -1; + } + memcpy(pluming.target.how, how, size); + pluming.target.how_size = size; + } + if (libexec_add_pluming(cmd, &pluming)) { + free(pluming.target.file); + free(pluming.target.how); + return -1; + } + return 0; + +#else + + errno = ENOSYS; + return -1; + +#endif +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commands.c b/libexec_pipe_commands.c new file mode 100644 index 0000000..580bf86 --- /dev/null +++ b/libexec_pipe_commands.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_pipe_commands(enum libexec_pipe how, ...) +{ + int ret; + va_list args; + va_start(args, how); + ret = libexec_vpipe_commands(how, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commandsv.c b/libexec_pipe_commandsv.c new file mode 100644 index 0000000..ee96b7d --- /dev/null +++ b/libexec_pipe_commandsv.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_pipe_commandsvn.c b/libexec_pipe_commandsvn.c new file mode 100644 index 0000000..d6aa1d5 --- /dev/null +++ b/libexec_pipe_commandsvn.c @@ -0,0 +1,106 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +static void +remove_last_pluming(struct libexec_command *cmd) +{ + if (!cmd->nplumings || !cmd->plumings) + abort(); + if (cmd->plumings[--cmd->nplumings].type != LIBEXEC_PLUMING_PIPE) + abort(); + close(cmd->plumings[cmd->nplumings].target.fd); + if (!cmd->nplumings) { + free(cmd->plumings); + cmd->plumings = NULL; + } +} + + +static int +pipe_two_commands(enum libexec_pipe how, struct libexec_command *left, struct libexec_command *right) +{ + int fds[2], r; + struct libexec_pluming tmp; + + if (!left || !right) { + einval: + errno = EINVAL; + return -1; + } + + if (how == LIBEXEC_PIPE) + r = pipe(fds); + else if (how == LIBEXEC_SOCK_STREAM) + r = socketpair(PF_UNIX, SOCK_STREAM, 0, fds); + else if (how == LIBEXEC_SOCK_SEQPACKET) + r = socketpair(PF_UNIX, SOCK_SEQPACKET, 0, fds); + else if (how == LIBEXEC_SOCK_DGRAM) + r = socketpair(PF_UNIX, SOCK_DGRAM, 0, fds); + else if (how == LIBEXEC_DIRECT_PIPE) + r = pipe2(fds, O_DIRECT); + else + goto einval; + if (r) + return -1; + + if (libexec_dup(left, STDOUT_FILENO, fds[1])) { + close(fds[0]); + close(fds[1]); + return -1; + } + left->plumings[left->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + if (libexec_dup(right, STDIN_FILENO, fds[0])) { + remove_last_pluming(left); + close(fds[0]); + return -1; + } + right->plumings[right->nplumings - 1].type = LIBEXEC_PLUMING_PIPE; + + /* pluming shall be at the front */ + tmp = left->plumings[left->nplumings - 1]; + memcpy(&left->plumings[1], &left->plumings[0], (left->nplumings - 1) * sizeof(*left->plumings)); + left->plumings[0] = tmp; + tmp = right->plumings[right->nplumings - 1]; + memcpy(&right->plumings[1], &right->plumings[0], (right->nplumings - 1) * sizeof(*right->plumings)); + right->plumings[0] = tmp; + + return 0; +} + + +int +libexec_pipe_commandsvn(enum libexec_pipe how, struct libexec_command *const *cmds, size_t n) +{ + enum libexec_pipe circular; + size_t i; + + circular = how; + circular &= LIBEXEC_PIPE_CIRCULARLY; + how ^= circular; + + for (i = 1; i < n; i++) + if (pipe_two_commands(how, cmds[i - 1], cmds[i])) + goto fail; + + if (circular && n) + if (pipe_two_commands(how, cmds[n - 1], cmds[0])) + goto fail; + + return 0; + +fail: + i -= 1; + while (i) { + remove_last_pluming(cmds[i--]); + remove_last_pluming(cmds[i]); + } + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_put_arguments.c b/libexec_put_arguments.c new file mode 100644 index 0000000..45f7f1a --- /dev/null +++ b/libexec_put_arguments.c @@ -0,0 +1,22 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_put_argumentsn.c b/libexec_put_argumentsn.c new file mode 100644 index 0000000..9d09ec0 --- /dev/null +++ b/libexec_put_argumentsn.c @@ -0,0 +1,45 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_put_argumentsn(struct libexec_command *cmd, const char *const *args, size_t nargs) +{ + size_t i; + void *new; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + new = realloc(cmd->arguments, (cmd->narguments + nargs + 1) * sizeof(*cmd->arguments)); + if (!new) + return -1; + cmd->arguments = new; + + for (i = 0; i < nargs; i++) { + cmd->arguments[cmd->narguments + i] = strdup(args[i]); + if (!cmd->arguments[cmd->narguments + i]) { + while (i) + free(cmd->arguments[cmd->narguments + --i]); + if (!cmd->narguments) { + free(cmd->arguments); + cmd->arguments = NULL; + } + return -1; + } + } + + cmd->arguments[cmd->narguments + nargs] = NULL; + cmd->narguments += nargs; + + return 0; + +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_putenv.c b/libexec_putenv.c new file mode 100644 index 0000000..366f5fb --- /dev/null +++ b/libexec_putenv.c @@ -0,0 +1,144 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv(struct libexec_command *cmd, enum libexec_insert_mode how, const char *string) +{ + char **env; + char *copy, *eqsign; + size_t i, n, len; + int environ_copied = 0; + void *new; + + if (!cmd || !string) { + errno = EINVAL; + return -1; + } + + env = cmd->environ ? cmd->environ : environ; + + if (how == LIBEXEC_APPEND || how == LIBEXEC_PREPEND) { + n = 0; + while (env[n]) + n++; + + just_add: + copy = strdup(string); + if (!copy) + return -1; + + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + environ_copied = 1; + } + + new = realloc(cmd->environ, (n + 2) * sizeof(*cmd->environ)); + if (!new) { + if (environ_copied) { + while (n) + free(cmd->environ[--n]); + free(cmd->environ); + cmd->environ = NULL; + } + free(copy); + return -1; + } + cmd->environ = new; + + if (how == LIBEXEC_APPEND) { + cmd->environ[n] = copy; + cmd->environ[n + 1] = NULL; + } else { + memmove(&cmd->environ[1], cmd->environ, (n + 1) * sizeof(*cmd->environ)); + cmd->environ[0] = copy; + } + + + } else { + eqsign = strchr(string, '='); + + if (!eqsign || (how != LIBEXEC_REPLACE && how != LIBEXEC_NOCLOBBER && how != LIBEXEC_NOREPLACE)) { + errno = EINVAL; + return -1; + } + + len = (size_t)(eqsign - string) + 1; + for (i = 0; env[i]; i++) + if (!strncmp(env[i], string, len)) + break; + + if (!env[i]) { + n = i; + how = LIBEXEC_APPEND; + goto just_add; + } + + if (how == LIBEXEC_NOREPLACE || !strcmp(env[i], string)) { + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + } + return 0; + } + if (how == LIBEXEC_NOCLOBBER) + return -1; /* do not change errno */ + + copy = strdup(string); + if (!copy) + return -1; + + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) { + free(copy); + return -1; + } + } + + free(cmd->environ[i]); + cmd->environ[i] = copy; + + } + return 0; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested via + * libexec_putenv_append.c, + * libexec_putenv_noclobber.c, + * libexec_putenv_noreplace.c, + * libexec_putenv_prepend.c, and + * libexec_putenv_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv(&cmd, (enum libexec_insert_mode)-1, "A=B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A=B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + return 0; +} + + +#endif diff --git a/libexec_putenv_append.c b/libexec_putenv_append.c new file mode 100644 index 0000000..bb45e1c --- /dev/null +++ b/libexec_putenv_append.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_append(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_APPEND, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_append(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenv_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "B=F"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_noclobber.c b/libexec_putenv_noclobber.c new file mode 100644 index 0000000..e9cf3ed --- /dev/null +++ b/libexec_putenv_noclobber.c @@ -0,0 +1,175 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_noclobber(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_NOCLOBBER, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_noreplace.c b/libexec_putenv_noreplace.c new file mode 100644 index 0000000..b33708a --- /dev/null +++ b/libexec_putenv_noreplace.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_noreplace(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_NOREPLACE, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_noreplace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_prepend.c b/libexec_putenv_prepend.c new file mode 100644 index 0000000..840978d --- /dev/null +++ b/libexec_putenv_prepend.c @@ -0,0 +1,235 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_prepend(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_PREPEND, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_prepend(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenv_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "X=0"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenv_replace.c b/libexec_putenv_replace.c new file mode 100644 index 0000000..c62ad01 --- /dev/null +++ b/libexec_putenv_replace.c @@ -0,0 +1,171 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenv_replace(struct libexec_command *cmd, const char *string) +{ + return libexec_putenv(cmd, LIBEXEC_REPLACE, string); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenv_replace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenv_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf.c b/libexec_putenvf.c new file mode 100644 index 0000000..7729fc4 --- /dev/null +++ b/libexec_putenvf.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf(cmd, how, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested, indirectly, via + * libexec_putenvf_append.c, + * libexec_putenvf_noclobber.c, + * libexec_putenvf_noreplace.c, + * libexec_putenvf_prepend.c, and + * libexec_putenvf_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, (enum libexec_insert_mode)-1, "A=%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A=%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + libexec_clear_environ(&cmd); + ASSERT_ZERO(libexec_putenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH=%s:%s", "test", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + errno = 0; + ASSERT_EQ_INT(libexec_putenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH=%s", "x"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + ASSERT_ZERO(libexec_putenvf(&cmd, LIBEXEC_REPLACE, "PATH=%s%s", "my", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "mypath"); + libexec_destroy_command(&cmd); + + return 0; +} + + +#endif diff --git a/libexec_putenvf_append.c b/libexec_putenvf_append.c new file mode 100644 index 0000000..8ceb806 --- /dev/null +++ b/libexec_putenvf_append.c @@ -0,0 +1,240 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_append(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_append(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_append(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s", "A=", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s%s%s", "B", "B", "=", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "B=%s", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s=%s=%s", "A", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "A%sD", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s", "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "%s%s%s", "A", "A=", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenvf_append(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "B=F"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_noclobber.c b/libexec_putenvf_noclobber.c new file mode 100644 index 0000000..988e381 --- /dev/null +++ b/libexec_putenvf_noclobber.c @@ -0,0 +1,180 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_noclobber(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_noclobber(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "%s", "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%s%s%s", "A", "=", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%s=%s", "A=B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "%s", "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%sB=F", "B")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "%sB=F", "")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noclobber(&cmd, "A=D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_noclobber(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_noreplace.c b/libexec_putenvf_noreplace.c new file mode 100644 index 0000000..bcde8a2 --- /dev/null +++ b/libexec_putenvf_noreplace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_noreplace(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_noreplace(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_noreplace(&cmd, "%s", "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s=%s", "A=B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "%s", "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_noreplace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_prepend.c b/libexec_putenvf_prepend.c new file mode 100644 index 0000000..2e51a72 --- /dev/null +++ b/libexec_putenvf_prepend.c @@ -0,0 +1,240 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_prepend(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_prepend(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_prepend(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s", "A=", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "AA%sE", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s%s%s", "B", "B", "=", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "B=%s", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[7]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s=%s=%s", "A", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "A%sD", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s", "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "%s%s%s", "A", "A=", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "X")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + ASSERT_ZERO(libexec_putenvf_prepend(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=D"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[7]); + ASSERT_EQ_STR(cmd.environ[7], "X=0"); + ASSERT_IS_NULL(cmd.environ[8]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_putenvf_replace.c b/libexec_putenvf_replace.c new file mode 100644 index 0000000..7cfd773 --- /dev/null +++ b/libexec_putenvf_replace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_putenvf_replace(struct libexec_command *cmd, const char *fmt, ...) +{ + int ret; + va_list args; + va_start(args, fmt); + ret = libexec_vputenvf_replace(cmd, fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(NULL, "X=Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(&cmd, NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_putenvf_replace(&cmd, "X"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s", "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s%s", "A=B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s=%s", "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "%s=E", "AA")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "BB%sF", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "A=D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "AA=E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "BB=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_putenvf_replace(&cmd, "B=F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_recv_document.c b/libexec_recv_document.c new file mode 100644 index 0000000..8eb0be6 --- /dev/null +++ b/libexec_recv_document.c @@ -0,0 +1,53 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_recv_document(struct libexec_document *doc) +{ + ssize_t r; + void *new; + size_t new_size; + + if (!doc) { + errno = EINVAL; + return -1; + } + + for (;;) { + if (doc->length == doc->alloc_size) { + new_size = doc->alloc_size + 8096; + new = realloc(doc->text, new_size); + if (!new) + return -1; + doc->text = new; + doc->alloc_size = new_size; + } + r = read(doc->fd, &doc->text[doc->length], doc->alloc_size - doc->length); + if (r <= 0) { + if (!r) + goto done; + return -1; + } + doc->length += (size_t)r; + } + return 0; + +done: + if (doc->length == doc->alloc_size) { + new_size = doc->alloc_size + 1; + new = realloc(doc->text, new_size); + if (!new) + return -1; + doc->text = new; + doc->alloc_size = new_size; + } + doc->text[doc->length] = '\0'; + return 1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_renumber_fd.c b/libexec_renumber_fd.c new file mode 100644 index 0000000..d9350fa --- /dev/null +++ b/libexec_renumber_fd.c @@ -0,0 +1,32 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_renumber_fd(struct libexec_command *cmd, int fd, int old_fd) +{ + if (fd == old_fd) { + if (!cmd || fd < 0) { + errno = EINVAL; + return -1; + } + return 0; + } + if (libexec_dup(cmd, fd, old_fd)) + return -1; + if (libexec_close(cmd, old_fd)) { + cmd->nplumings -= 1; + if (!cmd->nplumings) { + free(cmd->plumings); + cmd->plumings = NULL; + } + return -1; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_run.c b/libexec_run.c new file mode 100644 index 0000000..ca1f629 --- /dev/null +++ b/libexec_run.c @@ -0,0 +1,20 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_run(struct libexec_result *out, ...) +{ + int ret; + va_list args; + va_start(args, out); + ret = libexec_vrun(out, args); + va_end(args); + return ret; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_run_pipeline.c b/libexec_run_pipeline.c new file mode 100644 index 0000000..616f172 --- /dev/null +++ b/libexec_run_pipeline.c @@ -0,0 +1,343 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +struct reap_context { + pid_t *pids; + size_t npids; + size_t procs_rem; + int *exit_statuses_out; /* nullable */ +}; + + +struct reaped { + pid_t pid; + int status; +}; + + +static struct reaped *reaped_elsewhere = NULL; +static size_t nreaped_elsewhere = 0; +static size_t reaped_elsewhere_size = 0; + + +static void +sigchld_handler(int signo) +{ + (void) signo; +} + + +static int +unlist_reap(pid_t pid, int *status) +{ + size_t i; + + for (i = 0; i < nreaped_elsewhere; i++) + if (reaped_elsewhere[i].pid == pid) + goto found; + return 0; + +found: + *status = reaped_elsewhere[i].status; + reaped_elsewhere[i] = reaped_elsewhere[--nreaped_elsewhere]; + + if (nreaped_elsewhere <= reaped_elsewhere_size / 4) { + free(reaped_elsewhere); + reaped_elsewhere = NULL; + nreaped_elsewhere = 0; + reaped_elsewhere_size = 0; + } + + return 1; +} + + +static int +reap_child(struct reap_context *ctx, pid_t pid) +{ + size_t i; + int status; + + if (!ctx->procs_rem) + return 0; + + for (i = 0; i < ctx->npids; i++) { + if (ctx->pids[i] == pid) { + if (waitpid(pid, &status, WNOHANG) != pid) + return -1; + if (ctx->exit_statuses_out) + ctx->exit_statuses_out[i] = status; + ctx->procs_rem -= 1; + ctx->pids[i] = -1; + while (ctx->npids && ctx->pids[ctx->npids - 1] == -1) + ctx->npids -= 1; + return 1; + } + } + + return 0; +} + + +static int +reap_children(struct reap_context *reapctx, int flags, + int (*reap_mutex_control)(int action /* -1 = acquire, +1 = release */, void *user4), void *user4, + int (*on_alien_child_death)(pid_t pid, void *user2), void *user2) +{ + siginfo_t info; + int r, status; + void *new; + size_t new_size; + size_t i; + + if (reap_mutex_control) + if ((*reap_mutex_control)(-1, user4)) + goto fail; + + if (nreaped_elsewhere) { + for (i = 0; i < reapctx->npids; i++) { + if (unlist_reap(reapctx->pids[i], &status)) { + if (reapctx->exit_statuses_out) + reapctx->exit_statuses_out[i] = status; + reapctx->procs_rem -= 1; + reapctx->pids[i] = -1; + } + } + while (reapctx->npids && reapctx->pids[reapctx->npids - 1] == -1) + reapctx->npids -= 1; + } + + for (;;) { + info.si_pid = 0; + if (waitid(P_ALL, 0, &info, flags | WNOWAIT)) + goto fail; + if (info.si_pid < 1) + break; + + r = reap_child(reapctx, info.si_pid); + if (r < 0) + goto fail; + if (r > 0) + continue; + + if (on_alien_child_death) { + r = (*on_alien_child_death)(info.si_pid, user2); + if (r < 0) + goto fail; + if (r > 0) + continue; + } + + if (nreaped_elsewhere == reaped_elsewhere_size) { + new_size = reaped_elsewhere_size + 4; + new = realloc(reaped_elsewhere, new_size * sizeof(*reaped_elsewhere)); + if (!new) + goto fail; + reaped_elsewhere = new; + reaped_elsewhere_size = new_size; + } + + if (waitpid(info.si_pid, &status, WNOHANG) != info.si_pid) { + if (!nreaped_elsewhere) { + free(reaped_elsewhere); + reaped_elsewhere = NULL; + reaped_elsewhere_size = 0; + } + goto fail; + } + + reaped_elsewhere[nreaped_elsewhere].pid = info.si_pid; + reaped_elsewhere[nreaped_elsewhere].status = status; + nreaped_elsewhere++; + } + + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + goto fail; + return 0; + +fail: + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + goto fail; + return -1; +} + + +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) +{ + struct libexec_document *input_docs = NULL; + size_t i, ninput_docs = 0, files_open; + struct epoll_event evs[8]; + int epfd, n, sigchld_set = 0, error, r; + struct libexec_document *doc; + struct sigaction sigchld, old_sigchld; + struct reap_context reapctx = { + .pids = NULL, + .npids = 0, + .procs_rem = 0, + .exit_statuses_out = exit_statuses_out + }; + + if (!ncmds) + return 0; + + memset(evs, 0, sizeof(evs)); + + epfd = epoll_create1(EPOLL_CLOEXEC); + if (epfd < 0) + return -1; + + if (sigaction(SIGCHLD, NULL, &old_sigchld)) + goto fail; + if (!(old_sigchld.sa_flags & SA_SIGINFO)) { + if (old_sigchld.sa_handler == SIG_IGN || old_sigchld.sa_handler == SIG_DFL) { + memset(&sigchld, 0, sizeof(sigchld)); + sigchld.sa_handler = &sigchld_handler; + sigemptyset(&sigchld.sa_mask); + if (sigaction(SIGCHLD, &sigchld, NULL)) + goto fail; + sigchld_set = 1; + } + } + + if (alien_epoll >= 0) { + evs[0].events = EPOLLIN; + evs[0].data.ptr = NULL; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, alien_epoll, &evs[0])) + goto fail; + } + + reapctx.pids = calloc(ncmds, sizeof(*reapctx.pids)); + if (!reapctx.pids) + goto fail; + + for (i = 0; i < ncmds; i++) + if (libexec_spawn(&reapctx.pids[reapctx.npids++ /* may be set on failure */], cmds[i], + after_fork, user3, &input_docs, &ninput_docs, O_CLOEXEC | O_NONBLOCK)) + goto fail; + + for (i = 0; i < ninput_docs; i++) { + evs[0].events = EPOLLOUT; + evs[0].data.ptr = &input_docs[i]; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, input_docs[i].fd, &evs[0])) + goto fail; + } + + for (i = 0; i < ndocs; i++) { + doc = docs_1_level ? &(*docs)[i] : docs[i]; + evs[0].events = EPOLLIN; + evs[0].data.ptr = doc; + doc->user = 1; + if (epoll_ctl(epfd, EPOLL_CTL_ADD, doc->fd, &evs[0])) + goto fail; + } + + reapctx.procs_rem = ncmds; + + files_open = ninput_docs + ndocs + (size_t)(alien_epoll >= 0); + while (files_open) { + if (reap_children(&reapctx, WNOHANG, reap_mutex_control, user4, on_alien_child_death, user2)) + goto fail; + + n = epoll_pwait(epfd, evs, sizeof(evs) / sizeof(*evs), -1, sigmask); + if (n < 0) { + if (errno != EINTR) + goto fail; + if (on_interrupt) + if ((*on_interrupt)(user5)) + goto fail; + continue; + } + + for (i = 0; i < (size_t)n; i++) { + if (evs[i].data.ptr == NULL) { + r = (*on_alien_epoll)(alien_epoll, evs[i].events, user1); + if (r < 0) { + goto fail; + } else if (r > 0) { + if (epoll_ctl(epfd, EPOLL_CTL_DEL, alien_epoll, &evs[i])) + goto fail; + files_open -= 1; + } + } else { + doc = evs[i].data.ptr; + send_recv_again: + if (doc->user ? libexec_recv_document(doc) : libexec_send_document(doc)) { + if (errno == EAGAIN) + continue; + if (errno == EINTR) { + if (on_interrupt) + if ((*on_interrupt)(user5)) + goto fail; + goto send_recv_again; + } + } else { + files_open -= 1; + if (doc->user) { + close(doc->fd); + doc->fd = -1; + } else { + libexec_destroy_document(doc); + } + } + } + } + } + free(input_docs); + close(epfd); + + while (reapctx.procs_rem) + if (reap_children(&reapctx, 0, reap_mutex_control, user4, on_alien_child_death, user2)) + goto fail; + + free(reapctx.pids); + if (sigchld_set) + if (sigaction(SIGCHLD, &old_sigchld, NULL)) + abort(); + + return 0; + +fail: + error = errno; + while (ninput_docs) + libexec_destroy_document(&input_docs[--ninput_docs]); + free(input_docs); + close(epfd); + if (reapctx.npids) { + if (reap_mutex_control) + if ((*reap_mutex_control)(+1, user4)) + abort(); + for (i = 0; i < reapctx.npids; i++) + if (reapctx.pids[i] >= 1) + if (unlist_reap(reapctx.pids[i], &(int){0}) || kill(reapctx.pids[i], SIGKILL)) + reapctx.pids[i] = -1; + for (i = 0; i < reapctx.npids; i++) + if (reapctx.pids[i] >= 1) + waitpid(reapctx.pids[i], &(int){0}, 0); + if (reap_mutex_control) + if ((*reap_mutex_control)(-1, user4)) + abort(); + } + free(reapctx.pids); + if (sigchld_set) + if (sigaction(SIGCHLD, &old_sigchld, NULL)) + abort(); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_send_document.c b/libexec_send_document.c new file mode 100644 index 0000000..e69ad72 --- /dev/null +++ b/libexec_send_document.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_send_document(struct libexec_document *doc) +{ + ssize_t r; + if (!doc) { + errno = EINVAL; + return -1; + } + while (doc->offset < doc->length) { + r = write(doc->fd, &doc->text[doc->offset], doc->length - doc->offset); + if (r < 0) + return -1; + doc->offset += (size_t)r; + } + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_environ.c b/libexec_set_environ.c new file mode 100644 index 0000000..1d344d0 --- /dev/null +++ b/libexec_set_environ.c @@ -0,0 +1,26 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_set_environ(struct libexec_command *cmd, char **env) +{ + size_t i; + + if (!cmd) + return; + + if (cmd->environ) { + for (i = 0; cmd->environ[i]; i++) + free(cmd->environ[i]); + free(cmd->environ); + } + + cmd->environ = env; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_exec_fd.c b/libexec_set_exec_fd.c new file mode 100644 index 0000000..c185a9d --- /dev/null +++ b/libexec_set_exec_fd.c @@ -0,0 +1,21 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_exec_fd(struct libexec_command *cmd, int fd) +{ + if (!cmd || fd < 0) { + errno = EINVAL; + return -1; + } + cmd->exec_how = LIBEXEC_EXEC_FD; + cmd->exec_fd = fd; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_exec_path.c b/libexec_set_exec_path.c new file mode 100644 index 0000000..2b5a7af --- /dev/null +++ b/libexec_set_exec_path.c @@ -0,0 +1,29 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_exec_path(struct libexec_command *cmd, int dirfd, const char *path) +{ + char *s = NULL; + if (!cmd) { + errno = EINVAL; + return -1; + } + if (path) { + s = strdup(path); + if (!s) + return -1; + } + free(cmd->executable); + cmd->executable = s; + cmd->exec_fd = dirfd; + cmd->exec_how = LIBEXEC_EXEC_AT; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_executable.c b/libexec_set_executable.c new file mode 100644 index 0000000..0f9b7c9 --- /dev/null +++ b/libexec_set_executable.c @@ -0,0 +1,27 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_set_executable(struct libexec_command *cmd, const char *executable) +{ + char *s = NULL; + if (!cmd) { + errno = EINVAL; + return -1; + } + if (executable) { + s = strdup(executable); + if (!s) + return -1; + } + free(cmd->executable); + cmd->executable = s; + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_set_require_path.c b/libexec_set_require_path.c new file mode 100644 index 0000000..55dd369 --- /dev/null +++ b/libexec_set_require_path.c @@ -0,0 +1,18 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +void +libexec_set_require_path(struct libexec_command *cmd, int require) +{ + if (!cmd) + return; + cmd->exec_how = require ? LIBEXEC_REQUIRE_PATH : LIBEXEC_ALLOW_NAME; + cmd->exec_fd = -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_setenv.c b/libexec_setenv.c new file mode 100644 index 0000000..47bc8e9 --- /dev/null +++ b/libexec_setenv.c @@ -0,0 +1,73 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value) +{ + int ret; + char *buf; + if (!name || !value || strchr(name, '=')) { + errno = EINVAL; + return -1; + } + buf = malloc(strlen(name) + strlen(value) + 2); + stpcpy(stpcpy(stpcpy(buf, name), "="), value); + ret = libexec_putenv(cmd, how, buf); + free(buf); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested via + * libexec_setenv_append.c, + * libexec_setenv_noclobber.c, + * libexec_setenv_noreplace.c, + * libexec_setenv_prepend.c, and + * libexec_setenv_replace.c */ + + struct libexec_command cmd, ref; + int i; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)-1, "A", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + for (i = 0; i < LIBEXEC_INSERT_MODE__COUNT__; i++) { + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv(&cmd, (enum libexec_insert_mode)i, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + } + + return 0; +} + + +#endif diff --git a/libexec_setenv_append.c b/libexec_setenv_append.c new file mode 100644 index 0000000..a35207d --- /dev/null +++ b/libexec_setenv_append.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_append(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_APPEND, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "B=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenv_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_noclobber.c b/libexec_setenv_noclobber.c new file mode 100644 index 0000000..6478a9e --- /dev/null +++ b/libexec_setenv_noclobber.c @@ -0,0 +1,180 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_noclobber(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_NOCLOBBER, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_noreplace.c b/libexec_setenv_noreplace.c new file mode 100644 index 0000000..26339e8 --- /dev/null +++ b/libexec_setenv_noreplace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_noreplace(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_NOREPLACE, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_noreplace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_prepend.c b/libexec_setenv_prepend.c new file mode 100644 index 0000000..9c7a02b --- /dev/null +++ b/libexec_setenv_prepend.c @@ -0,0 +1,212 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_prepend(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_PREPEND, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_prepend(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenv_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenv_replace.c b/libexec_setenv_replace.c new file mode 100644 index 0000000..f8191c1 --- /dev/null +++ b/libexec_setenv_replace.c @@ -0,0 +1,176 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenv_replace(struct libexec_command *cmd, const char *name, const char *value) +{ + return libexec_setenv(cmd, LIBEXEC_REPLACE, name, value); +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenv_replace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenv_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf.c b/libexec_setenvf.c new file mode 100644 index 0000000..7589dca --- /dev/null +++ b/libexec_setenvf.c @@ -0,0 +1,61 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf(cmd, how, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + /* Correct usage is tested, indirectly, via + * libexec_setenvf_append.c, + * libexec_setenvf_noclobber.c, + * libexec_setenvf_noreplace.c, + * libexec_setenvf_prepend.c, and + * libexec_setenvf_replace.c */ + + struct libexec_command cmd, ref; + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, (enum libexec_insert_mode)-1, "A", "%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, (enum libexec_insert_mode)LIBEXEC_INSERT_MODE__COUNT__, "A", "%s", "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + libexec_clear_environ(&cmd); + ASSERT_ZERO(libexec_setenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH", "%s:%s", "test", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + errno = 0; + ASSERT_EQ_INT(libexec_setenvf(&cmd, LIBEXEC_NOCLOBBER, "PATH", "%s", "x"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "test:path"); + ASSERT_ZERO(libexec_setenvf(&cmd, LIBEXEC_REPLACE, "PATH", "%s%s", "my", "path")); + ASSERT_EQ_STR(libexec_getenv(&cmd, "PATH"), "mypath"); + libexec_destroy_command(&cmd); + + return 0; +} + + +#endif diff --git a/libexec_setenvf_append.c b/libexec_setenvf_append.c new file mode 100644 index 0000000..b0b6a4e --- /dev/null +++ b/libexec_setenvf_append.c @@ -0,0 +1,217 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_append(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_append(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "B=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenvf_append(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "B=F"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_noclobber.c b/libexec_setenvf_noclobber.c new file mode 100644 index 0000000..f40b367 --- /dev/null +++ b/libexec_setenvf_noclobber.c @@ -0,0 +1,185 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_noclobber(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_noclobber(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s%s", "B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "B%sC", "=")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noclobber(&cmd, "A", "D"), -1); + ASSERT_EQ_INT(errno, 0); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_noclobber(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_noreplace.c b/libexec_setenvf_noreplace.c new file mode 100644 index 0000000..9480c7f --- /dev/null +++ b/libexec_setenvf_noreplace.c @@ -0,0 +1,181 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_noreplace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_noreplace(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_noreplace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s%s", "B", "=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_noreplace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_prepend.c b/libexec_setenvf_prepend.c new file mode 100644 index 0000000..3a8a197 --- /dev/null +++ b/libexec_setenvf_prepend.c @@ -0,0 +1,217 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_prepend(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_prepend(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_prepend(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_append(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "AA", "%i", 5)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "B", "F%s", "")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=5"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[6]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "X=0"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "X=0"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "X=0"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "X=0"); + ASSERT_IS_NULL(cmd.environ[5]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "BB", "%s%s", "", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "A=D"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "X=0"); + ASSERT_IS_NULL(cmd.environ[6]); + + ASSERT_ZERO(libexec_setenvf_prepend(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "B=F"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "A=D"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "A=D"); + ASSERT_NOT_NULL(cmd.environ[5]); + ASSERT_EQ_STR(cmd.environ[5], "A=B=C"); + ASSERT_NOT_NULL(cmd.environ[6]); + ASSERT_EQ_STR(cmd.environ[6], "X=0"); + ASSERT_IS_NULL(cmd.environ[7]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_setenvf_replace.c b/libexec_setenvf_replace.c new file mode 100644 index 0000000..9be28db --- /dev/null +++ b/libexec_setenvf_replace.c @@ -0,0 +1,181 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_setenvf_replace(struct libexec_command *cmd, const char *name, const char *value_fmt, ...) +{ + int ret; + va_list args; + va_start(args, value_fmt); + ret = libexec_vsetenvf_replace(cmd, name, value_fmt, args); + va_end(args); + return ret; +} + + +#else + + +int +main(void) +{ + struct libexec_command cmd, ref; + char **env; + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(NULL, "X", "Y"), -1); + ASSERT_EQ_INT(errno, EINVAL); + + libexec_init_command(&cmd); + memcpy(&ref, &cmd, sizeof(cmd)); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, "A=B", "C"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, "A", NULL), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + errno = 0; + ASSERT_EQ_INT(libexec_setenvf_replace(&cmd, NULL, "B"), -1); + ASSERT_EQ_INT(errno, EINVAL); + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + + ASSERT_ZERO(libexec_clear_environ(&cmd)); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_IS_NULL(cmd.environ[0]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s=%s", "B", "C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%c", 'D')); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_IS_NULL(cmd.environ[1]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "A=D"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "B=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + env = cmd.environ; + cmd.environ = NULL; + ASSERT_IS_TRUE(!memcmp(&cmd, &ref, sizeof(cmd))); + cmd.environ = env; + + libexec_destroy_command(&cmd); + + libexec_init_command(&cmd); + env = environ; + environ = calloc(2, sizeof(*environ)); + ASSERT_NOT_NULL(environ); + environ[0] = strdup("X=0"); + ASSERT_NOT_NULL(environ[0]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "%s", "B=C")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=B=C"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "A", "D")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_IS_NULL(cmd.environ[2]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "AA", "E")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_IS_NULL(cmd.environ[3]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "BB", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_IS_NULL(cmd.environ[4]); + + ASSERT_ZERO(libexec_setenvf_replace(&cmd, "B", "F")); + ASSERT_NOT_NULL(cmd.environ); + ASSERT_NOT_NULL(cmd.environ[0]); + ASSERT_EQ_STR(cmd.environ[0], "X=0"); + ASSERT_NOT_NULL(cmd.environ[1]); + ASSERT_EQ_STR(cmd.environ[1], "A=D"); + ASSERT_NOT_NULL(cmd.environ[2]); + ASSERT_EQ_STR(cmd.environ[2], "AA=E"); + ASSERT_NOT_NULL(cmd.environ[3]); + ASSERT_EQ_STR(cmd.environ[3], "BB=F"); + ASSERT_NOT_NULL(cmd.environ[4]); + ASSERT_EQ_STR(cmd.environ[4], "B=F"); + ASSERT_IS_NULL(cmd.environ[5]); + + libexec_destroy_command(&cmd); + environ = env; + return 0; +} + + +#endif diff --git a/libexec_spawn.c b/libexec_spawn.c new file mode 100644 index 0000000..f0b46e6 --- /dev/null +++ b/libexec_spawn.c @@ -0,0 +1,89 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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 fds[2], error, new_fd; + size_t i; + ssize_t r; + + if (!docsp || !ndocsp || !out || !cmd) { + errno = EINVAL; + return -1; + } + + *out = -1; + + if (pipe2(fds, O_CLOEXEC)) + return -1; + + new_fd = fds[1]; + for (i = 0; i < cmd->nplumings; i++) + if (cmd->plumings[i].fd >= new_fd) + new_fd = cmd->plumings[i].fd + 1; + + if (new_fd != fds[1]) { + new_fd = fcntl(fds[1], F_DUPFD_CLOEXEC, new_fd); + if (new_fd == -1) + goto fail; + fds[1] = new_fd; + } + + if (libexec_get_documents(cmd, docsp, ndocsp, doc_fd_flags)) + goto fail; + + *out = fork(); + switch (*out) { + case -1: + goto fail; + + case 0: + close(fds[0]); + if (after_fork) + if ((*after_fork)(cmd, fds[1], user)) + goto child_fail; + libexec_exec(cmd); + child_fail: + error = errno; + write_again: + r = write(fds[1], &error, sizeof(error)); + if (r <= 0) { + if (errno == EINTR) + goto write_again; + abort(); + } + _exit(1); + + default: + close(fds[1]); + read_again: + r = read(fds[0], &error, sizeof(error)); + if (r < 0) { + if (errno == EINTR) + goto read_again; + } else if (r == 0) { + return 0; + } else if ((size_t)r >= sizeof(error)) { + errno = error; + } + + return -1; + } + +fail: + error = errno; + close(fds[0]); + close(fds[1]); + errno = error; + return -1; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv.c b/libexec_unsetenv.c new file mode 100644 index 0000000..48ec645 --- /dev/null +++ b/libexec_unsetenv.c @@ -0,0 +1,48 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + n = 0; + while (env[n]) + n++; + + for (i = 0; i < n;) { + if (!strncmp(env[i], name, len) && env[i][len] == '=') { + if (!cmd->environ) { + if (libexec_copy_environ(cmd, NULL)) + return -1; + env = cmd->environ; + } + free(env); + memmove(&env[i], &env[i + 1], (n - i - 1) * sizeof(*env)); + n--; + } else { + i++; + } + } + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv_first.c b/libexec_unsetenv_first.c new file mode 100644 index 0000000..9c5ea12 --- /dev/null +++ b/libexec_unsetenv_first.c @@ -0,0 +1,44 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv_first(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + for (i = 0; env[i]; i++) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + break; + + if (env[i]) { + n = i; + while (env[n]) + n++; + if (!cmd->environ) + if (libexec_copy_environ(cmd, NULL)) + return -1; + free(cmd->environ[i]); + memmove(&cmd->environ[i], &cmd->environ[i + 1], (n - i - 1) * sizeof(*cmd->environ)); + } + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_unsetenv_last.c b/libexec_unsetenv_last.c new file mode 100644 index 0000000..e3fc263 --- /dev/null +++ b/libexec_unsetenv_last.c @@ -0,0 +1,46 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_unsetenv_last(struct libexec_command *cmd, const char *name) +{ + char **env; + size_t i, n, len; + + if (!cmd || !name) { + errno = EINVAL; + return -1; + } + + if (strchr(name, '=')) + return 0; + + env = cmd->environ ? cmd->environ : environ; + len = strlen(name); + + n = 0; + while (env[n]) + n++; + + for (i = n; i--;) + if (!strncmp(env[i], name, len) && env[i][len] == '=') + goto found; + + return 0; + +found: + if (!cmd->environ) + if (libexec_copy_environ(cmd, NULL)) + return -1; + free(cmd->environ[i]); + memmove(&cmd->environ[i], &cmd->environ[i + 1], (n - i - 1) * sizeof(*cmd->environ)); + + return 0; +} + + +#else +LIBEXEC_CONST__ int main(void) {return 0;} /* TODO test */ +#endif diff --git a/libexec_vconstruct_command.c b/libexec_vconstruct_command.c new file mode 100644 index 0000000..bb3626f --- /dev/null +++ b/libexec_vconstruct_command.c @@ -0,0 +1,882 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#define FLAG_MINUS 0x01 +#define FLAG_PLUS 0x02 +#define FLAG_HASH 0x04 +#define FLAG_ZERO 0x08 +#define FLAG_SPACE 0x10 + + +#define TYPESIZE_CHAR -2 +#define TYPESIZE_SHORT -1 +#define TYPESIZE_INT 0 +#define TYPESIZE_LONG +1 +#define TYPESIZE_LONG_LONG +2 +#define TYPESIZE_SIZE 3 +#define TYPESIZE_PTRDIFF 4 +#define TYPESIZE_INTPTR 5 +#define TYPESIZE_INTMAX 6 + + +union anyarray_const { + const void *v; + const signed char *hhi; + const signed short int *hi; + const signed int *i; + const signed long int *li; + const signed long long int *lli; + const ssize_t *zi; + const ptrdiff_t *ti; + const intptr_t *pi; + const intmax_t *ji; + const unsigned char *hhu; + const unsigned short int *hu; + const unsigned int *u; + const unsigned long int *lu; + const unsigned long long int *llu; + const size_t *zu; + const ptrdiff_t *tu; + const uintptr_t *pu; + const uintmax_t *ju; + const char *c; + const char *const *s; +}; + +union anyvalue { + intmax_t i; + uintmax_t u; + char c[2]; + const char *s; +}; + +struct partial_string { + char *s; + size_t len; + size_t size; + int started; +}; + +struct argument_format { + size_t array_size; + size_t width1; + size_t width2; + int array_input; + int array_null_terminated; + int typesize; + int has_dot; + char typeclass; + unsigned flags; +}; + +struct ignore_stack { + char *stack; + size_t top; + size_t size; + size_t active; +}; + + +static int +resize_partial_string(struct partial_string *str, size_t req_size) +{ + void *new = realloc(str->s, req_size); + if (!new) + return -1; + str->s = new; + str->size = req_size; + return 0; +} + + +static int +resize_partial_string_if_full(struct partial_string *str, size_t extra) +{ + if (str->len == str->size) + if (resize_partial_string(str, str->len + extra)) + return -1; + return 0; +} + + +static char * +make_unsigned(uintmax_t value, char fmt, const char *plus, const char *prefix, size_t minwidth) +{ +#if defined(__GNUC__) +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + const char *fmtstr; + char *ret, *p; + size_t len, zeroes, totlen; + + if (fmt == 'd' || fmt == 'i' || fmt == 'u') + fmtstr = "%ju"; + else if (fmt == 'o') + fmtstr = "%jo"; + else if (fmt == 'x') + fmtstr = "%jx"; + else if (fmt == 'X') + fmtstr = "%jX"; + else + abort(); + + len = (size_t)snprintf(NULL, 0, fmtstr, value); + zeroes = len < minwidth ? minwidth - len : 0; + + totlen = strlen(plus) + 1; + if (strlen(prefix) > SIZE_MAX - totlen) + goto enomem; + totlen += strlen(prefix); + if (zeroes + len > SIZE_MAX - totlen) + goto enomem; + totlen += zeroes + len; + + ret = malloc(totlen); + if (!ret) + goto enomem; + + p = stpcpy(stpcpy(ret, plus), prefix); + while (zeroes--) + *p++ = '0'; + sprintf(p, fmtstr, value); + + return ret; + +enomem: + errno = ENOMEM; + return NULL; + +#if defined(__GNUC__) +# pragma GCC diagnostic pop +#endif +} + + +static char * +make_signed(intmax_t value, char fmt, const char *plus, const char *prefix, size_t minwidth) +{ + if (value < 0) + return make_unsigned((uintmax_t)-(value + 1) + 1U, fmt, "-", prefix, minwidth); + else + return make_unsigned((uintmax_t)value, fmt, plus, prefix, minwidth); +} + + +static unsigned +get_flag(char c) +{ + return c == '-' ? FLAG_MINUS : + c == '+' ? FLAG_PLUS : + c == '#' ? FLAG_HASH : + c == '0' ? FLAG_ZERO : + c == ' ' ? FLAG_SPACE : 0U; +} + + +static unsigned +remove_ignored_flags(unsigned flags, char fmt) +{ + if (fmt == 's' || fmt == 'c') { + flags &= FLAG_MINUS; + } else if (fmt == 'i' || fmt == 'd') { + if (flags & FLAG_PLUS) + flags &= (unsigned)~FLAG_SPACE; + } else { + flags &= (unsigned)~(FLAG_PLUS | FLAG_SPACE); + } + return flags; +} + + +static const char * +read_size(size_t *out, const char *fmt, va_list args, int optional) +{ + size_t digit; + + if (*fmt == '*') { + fmt++; + *out = va_arg(args, size_t); + } else if (!optional && !isdigit(*fmt)) { + goto einval; + } else { + *out = 0; + while (isdigit(*fmt)) { + digit = (size_t)(*fmt++ - '0'); + if (*out > (SIZE_MAX - digit) / 10U) + goto einval; + *out = *out * 10U + digit; + } + } + + return fmt; + +einval: + errno = EINVAL; + return NULL; +} + + +static const char * +get_typesize(int *out, const char *fmt) +{ + *out = 0; + + for (; *fmt == 'h'; fmt++) + if (--*out < -2) + goto einval; + + for (; *fmt == 'l'; fmt++) + if (*out < 0 || ++*out > +2) + goto einval; + + if (*fmt == 'z' || *fmt == 't' || *fmt == 'T' || *fmt == 'j') { + if (*out) + goto einval; + *out = *fmt == 'z' ? TYPESIZE_SIZE : + *fmt == 't' ? TYPESIZE_PTRDIFF : + *fmt == 'T' ? TYPESIZE_INTPTR : /* non-standard */ + TYPESIZE_INTMAX; + fmt++; + } + + /* + * TODO add =N for intN_t/uintN_t (arbitrary N) + * TODO add =+N for int_leastN_t/uint_leastN_t (arbitrary N) + * TODO add =/N for int_fastN_t/uint_fastN_t (arbitrary N) + * non-standard + */ + + if (*fmt == 's' || *fmt == 'c') { + if (*out) { + /* TODO %ls should be `const wchar_t *` */ + /* TODO %lc should be `wint_t` */ + goto einval; + } + } + + return fmt; + +einval: + errno = EINVAL; + return NULL; +} + + +static intmax_t +get_signed_va(va_list args, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (intmax_t)(signed char)va_arg(args, signed int); + else if (typesize == TYPESIZE_SHORT) + return (intmax_t)(signed short int)va_arg(args, signed int); + else if (typesize == TYPESIZE_INT) + return (intmax_t)va_arg(args, signed int); + else if (typesize == TYPESIZE_LONG) + return (intmax_t)va_arg(args, signed long int); + else if (typesize == TYPESIZE_LONG_LONG) + return (intmax_t)va_arg(args, signed long long int); + else if (typesize == TYPESIZE_SIZE) /* TODO since ssize_t may be bounded to -1, may it be bounded above INTMAX_MAX? */ + return (intmax_t)va_arg(args, ssize_t); + else if (typesize == TYPESIZE_PTRDIFF) + return (intmax_t)va_arg(args, ptrdiff_t); + else if (typesize == TYPESIZE_INTPTR) + return (intmax_t)va_arg(args, intptr_t); + else if (typesize == TYPESIZE_INTMAX) + return (intmax_t)va_arg(args, intmax_t); + else + abort(); +} + + +static uintmax_t +get_unsigned_va(va_list args, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (uintmax_t)(unsigned char)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_SHORT) + return (uintmax_t)(unsigned short int)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_INT) + return (uintmax_t)va_arg(args, unsigned int); + else if (typesize == TYPESIZE_LONG) + return (uintmax_t)va_arg(args, unsigned long int); + else if (typesize == TYPESIZE_LONG_LONG) + return (uintmax_t)va_arg(args, unsigned long long int); + else if (typesize == TYPESIZE_SIZE) + return (uintmax_t)va_arg(args, size_t); + else if (typesize == TYPESIZE_PTRDIFF) + return (uintmax_t)va_arg(args, ptrdiff_t); /* TODO ptrdiff_t is signed */ + else if (typesize == TYPESIZE_INTPTR) + return (uintmax_t)va_arg(args, uintptr_t); + else if (typesize == TYPESIZE_INTMAX) + return (uintmax_t)va_arg(args, uintmax_t); + else + abort(); +} + + +static intmax_t +get_signed_array(union anyarray_const *arrayp, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (intmax_t)*arrayp->hhi++; + else if (typesize == TYPESIZE_SHORT) + return (intmax_t)*arrayp->hi++; + else if (typesize == TYPESIZE_INT) + return (intmax_t)*arrayp->i++; + else if (typesize == TYPESIZE_LONG) + return (intmax_t)*arrayp->li++; + else if (typesize == TYPESIZE_LONG_LONG) + return (intmax_t)*arrayp->lli++; + else if (typesize == TYPESIZE_SIZE) + return (intmax_t)*arrayp->zi++; /* TODO since ssize_t may be bounded to -1, may it be bounded above INTMAX_MAX? */ + else if (typesize == TYPESIZE_PTRDIFF) + return (intmax_t)*arrayp->ti++; + else if (typesize == TYPESIZE_INTPTR) + return (intmax_t)*arrayp->pi++; + else if (typesize == TYPESIZE_INTMAX) + return (intmax_t)*arrayp->ji++; + else + abort(); +} + + +static uintmax_t +get_unsigned_array(union anyarray_const *arrayp, int typesize) +{ + if (typesize == TYPESIZE_CHAR) + return (uintmax_t)*arrayp->hhu++; + else if (typesize == TYPESIZE_SHORT) + return (uintmax_t)*arrayp->hu++; + else if (typesize == TYPESIZE_INT) + return (uintmax_t)*arrayp->u++; + else if (typesize == TYPESIZE_LONG) + return (uintmax_t)*arrayp->lu++; + else if (typesize == TYPESIZE_LONG_LONG) + return (uintmax_t)*arrayp->llu++; + else if (typesize == TYPESIZE_SIZE) + return (uintmax_t)*arrayp->zu++; + else if (typesize == TYPESIZE_PTRDIFF) + return (uintmax_t)*arrayp->tu++; /* TODO ptrdiff_t is signed */ + else if (typesize == TYPESIZE_INTPTR) + return (uintmax_t)*arrayp->pu++; + else if (typesize == TYPESIZE_INTMAX) + return (uintmax_t)*arrayp->ju++; + else + abort(); +} + + +static int +get_value_va(union anyvalue *out, va_list args, char fmt, int typesize) +{ + if (fmt == 'i' || fmt == 'd') { + out->i = get_signed_va(args, typesize); + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + out->u = get_unsigned_va(args, typesize); + + } else if (fmt == 's') { + if (typesize) + abort(); + out->s = va_arg(args, const char *); + + } else if (fmt == 'c') { + if (typesize) + abort(); + out->c[0] = (char)va_arg(args, int); + out->c[1] = '\0'; + + } else { + errno = EINVAL; + return -1; + } + + return 0; +} + + +static int +get_value_array(union anyvalue *out, union anyarray_const *arrayp, char fmt, int typesize) +{ + if (fmt == 'i' || fmt == 'd') { + out->i = get_signed_array(arrayp, typesize); + return !!out->i; + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + out->u = get_unsigned_array(arrayp, typesize); + return !!out->u; + + } else if (fmt == 's') { + if (typesize) + abort(); + out->s = *arrayp->s++; + return !!out->s; + + } else if (fmt == 'c') { + if (typesize) + abort(); + out->c[0] = *arrayp->c++; + out->c[1] = '\0'; + return !!out->c[0]; + + } else { + errno = EINVAL; + return -1; + } +} + + +static char * +create_string(const union anyvalue *value, char fmt, const char *plus, const char *prefix, int has_dot, size_t width) +{ + if (fmt == 'i' || fmt == 'd') { + return make_signed(value->i, fmt, plus, value->i ? prefix : "", width); + + } else if (fmt == 'u' || fmt == 'o' || fmt == 'x' || fmt == 'X') { + return make_unsigned(value->u, fmt, "", value->u ? prefix : "", width); + + } else if (fmt == 's') { + return strndup(value->s, has_dot ? width : strlen(value->s)); + + } else if (fmt == 'c') { + return strndup(value->c, has_dot ? width : 1); + + } else { + errno = EINVAL; + return NULL; + } +} + + +static int +pad_and_insert(struct partial_string *arg, const char *substring, size_t min_width, char pad_char, unsigned pad_before) +{ + size_t pad_len, substring_len; + + substring_len = strlen(substring); + pad_len = substring_len < min_width ? min_width - substring_len : 0; + if (substring_len + pad_len > SIZE_MAX - arg->len) { + errno = ENOMEM; + return -1; + } + + if (substring_len + pad_len + arg->len > arg->size) + if (resize_partial_string(arg, substring_len + pad_len + arg->len)) + return -1; + + if (pad_before) { + memset(&arg->s[arg->len], pad_char, pad_len); + arg->len += pad_len; + } + + memcpy(&arg->s[arg->len], substring, substring_len); + arg->len += substring_len; + + if (!pad_before) { + memset(&arg->s[arg->len], pad_char, pad_len); + arg->len += pad_len; + } + + return 0; +} + + +static int +insert_argument(struct partial_string *arg, const union anyvalue *value, char fmt, + unsigned flags, size_t width1, int has_dot, size_t width2) +{ + const char *plus, *prefix; + char *substring; + + plus = (flags & FLAG_PLUS) ? "+" : (flags & FLAG_SPACE) ? " " : ""; + + prefix = !(flags & FLAG_HASH) ? "" : + fmt == 'o' ? "0" : + fmt == 'x' ? "0x" : + fmt == 'X' ? "0X" : ""; + + if (fmt != 's' && fmt != 'c') { + if (!has_dot) { + width2 = width1; + width1 = 0; + } + } + + substring = create_string(value, fmt, plus, prefix, has_dot, width2); + if (!substring) + return -1; + + if (pad_and_insert(arg, substring, width1, (flags & FLAG_ZERO) ? '0' : ' ', flags & FLAG_MINUS)) { + free(substring); + return -1; + } + + free(substring); + return 0; +} + + +static int +get_and_insert_arguments(struct partial_string *arg, va_list args, const struct argument_format *arg_fmt, int ignoring) +{ + union anyarray_const array; + union anyvalue value; + unsigned int flags; + size_t n; + int r; + + flags = remove_ignored_flags(arg_fmt->flags, arg_fmt->typeclass); + + if (arg_fmt->array_input) { + array.v = va_arg(args, const void *); + n = arg_fmt->array_size; + while (arg_fmt->array_null_terminated || n--) { + r = get_value_array(&value, &array, arg_fmt->typeclass, arg_fmt->typesize); + if (r < 0) + return -1; + else if (!r && arg_fmt->array_null_terminated) + break; + if (!ignoring) + if (insert_argument(arg, &value, arg_fmt->typeclass, flags, + arg_fmt->width1, arg_fmt->has_dot, arg_fmt->width2)) + return -1; + } + + } else { + if (get_value_va(&value, args, arg_fmt->typeclass, arg_fmt->typesize)) + return -1; + if (!ignoring) + if (insert_argument(arg, &value, arg_fmt->typeclass, flags, + arg_fmt->width1, arg_fmt->has_dot, arg_fmt->width2)) + return -1; + } + + return 0; +} + + +static const char * +get_argument_format(struct argument_format *out, const char *fmt, va_list args) +{ + unsigned flag; + + memset(out, 0, sizeof(*out)); + + if (*fmt == '[') { + fmt++; + out->array_input = 1; + if (*fmt == ']') { + out->array_null_terminated = 1; + } else { + fmt = read_size(&out->array_size, fmt, args, 1); + if (!fmt) + goto fail; + } + if (*fmt != ']') + goto einval; + fmt++; + } + + while ((flag = get_flag(*fmt))) { + out->flags |= flag; + fmt++; + } + + fmt = read_size(&out->width1, fmt, args, 0); + if (!fmt) + goto fail; + if (*fmt == '.') { + out->has_dot = 1; + out->flags &= (unsigned)~FLAG_ZERO; + fmt++; + fmt = read_size(&out->width2, fmt, args, 0); + if (!fmt) + goto fail; + } + + fmt = get_typesize(&out->typesize, fmt); + if (!fmt) + goto fail; + + out->typeclass = *fmt++; + + return fmt; + +einval: + errno = EINVAL; +fail: + return NULL; +} + + +static void +destroy_ignore_stack(struct ignore_stack *stack) +{ + free(stack->stack); + stack->stack = NULL; +} + + +static int +push_ignore_stack(struct ignore_stack *stack, va_list args) +{ + int show; + void *new; + size_t new_size; + + if (stack->top == stack->size) { + new_size = stack->size + 8; + new = realloc(stack->stack, new_size * sizeof(*stack->stack)); + if (!new) + return -1; + stack->stack = new; + stack->size = new_size; + } + + show = va_arg(args, int); + stack->stack[stack->top++] = (char)!show; + stack->active += (size_t)!show; + return 0; +} + + +static int +pop_ignore_stack(struct ignore_stack *stack) +{ + if (!stack->top) { + errno = EINVAL; + return -1; + } + if (stack->stack[--stack->top]) + stack->active -= 1; + return 0; +} + + +static int +is_ignoring(struct ignore_stack *stack) +{ + return stack->active > 0; +} + + +static const char * +format(const char *fmt, struct partial_string *arg, struct ignore_stack *ignore_stack, va_list args) +{ + + struct argument_format arg_fmt; + + /* TODO $< should work as <(), * or number before < to select fd (default: STDOUT_FILENO) */ + /* TODO $> should work as >(), * or number before > to select fd (default: STDIN_FILENO) */ + /* TODO add support for embedding via %S and %C (do not set arg->started) */ + /* TODO add support for floating point values */ + + if (*fmt == '$' || *fmt == '\'' || *fmt == '"' || *fmt == '_') { + if (!is_ignoring(ignore_stack)) { + if (arg->len == arg->size) + if (resize_partial_string(arg, arg->size + 32)) + goto fail; + arg->s[arg->len++] = *fmt == '_' ? ' ' : *fmt; + arg->started = 1; + } + fmt++; + + } else if (*fmt == '(') { + fmt++; + if (push_ignore_stack(ignore_stack, args)) + goto fail; + + } else if (*fmt == ')') { + fmt++; + if (pop_ignore_stack(ignore_stack)) + goto fail; + + } else { + fmt = get_argument_format(&arg_fmt, fmt, args); + if (!fmt) + goto fail; + if (get_and_insert_arguments(arg, args, &arg_fmt, is_ignoring(ignore_stack))) + goto fail; + arg->started = 1; + } + + return fmt; + +fail: + return NULL; +} + + +static char ** +format_command(const char *fmt, va_list args, size_t *narguments_out) +{ + size_t command_size = 0; + char **command = NULL; + struct ignore_stack ignore_stack; + struct partial_string arg; + void *new; + + *narguments_out = 0; + + memset(&ignore_stack, 0, sizeof(ignore_stack)); + ignore_stack.stack = NULL; + + memset(&arg, 0, sizeof(arg)); + arg.s = NULL; + +next_arg: + arg.started = 0; + arg.len = 0; + while (*fmt) { + if (*fmt == '$') { + fmt++; + fmt = format(fmt, &arg, &ignore_stack, args); + if (!fmt) + goto fail; + + } else if (isspace(*fmt)) { + fmt++; + if (!arg.started || is_ignoring(&ignore_stack)) + continue; + new = realloc(command, (command_size + 1) * sizeof(*command)); + if (!new) + goto fail; + command = new; + if (resize_partial_string_if_full(&arg, 32)) + goto fail; + arg.s[arg.len] = '\0'; + command[command_size] = strdup(arg.s); + if (!command[command_size]) + goto fail; + command_size++; + goto next_arg; + + } else if (*fmt == '\'' || *fmt == '"') { + arg.started = 1; + /* TODO add support for quotes */ + errno = EINVAL; + goto fail; + + } else { + if (!is_ignoring(&ignore_stack)) { + if (resize_partial_string_if_full(&arg, 32)) + goto fail; + arg.s[arg.len++] = *fmt; + arg.started = 1; + } + fmt++; + } + } + + new = realloc(command, (command_size + 2) * sizeof(*command)); + if (!new) + goto fail; + command = new; + if (arg.started) { + if (resize_partial_string_if_full(&arg, 1)) + goto fail; + arg.s[arg.len] = '\0'; + command[command_size++] = arg.s; + } else { + free(arg.s); + } + command[command_size] = NULL; + destroy_ignore_stack(&ignore_stack); + + *narguments_out = command_size; + return command; + +fail: + free(arg.s); + destroy_ignore_stack(&ignore_stack); + while (command_size) + free(command[--command_size]); + free(command); + return NULL; +} + + +static int +append_arguments(struct libexec_command *cmd, va_list args) +{ + size_t n = 0, i; + va_list args2; + void *new; + + va_copy(args2, args); + while (va_arg(args2, const char *)) + n += 1; + va_end(args2); + + new = realloc(cmd->arguments, (cmd->narguments + n + 1) * sizeof(*cmd->arguments)); + if (!new) + return -1; + cmd->arguments = new; + + for (i = 0; i < n; i++) { + cmd->arguments[cmd->narguments + i] = strdup(va_arg(args, const char *)); + if (!cmd->arguments[cmd->narguments + i]) { + while (i) + free(cmd->arguments[cmd->narguments + --i]); + if (!cmd->narguments) { + free(cmd->arguments); + cmd->arguments = NULL; + } + return -1; + } + } + + cmd->arguments[cmd->narguments + n] = va_arg(args, char *); /* that's a NULL */ + cmd->narguments += n; + + return 0; +} + + +int +libexec_vconstruct_command(struct libexec_command *cmd, const char *fmt, va_list args) +{ + size_t n; + char **command = NULL; + void *new; + + if (!cmd) { + errno = EINVAL; + return -1; + } + + if (!fmt) { + return append_arguments(cmd, args); + + } else { + command = format_command(fmt, args, &n); + if (!command) + return -1; + if (!cmd->narguments) { + cmd->arguments = command; + cmd->narguments = n; + } else { + new = realloc(cmd->arguments, (cmd->narguments + n + 1) * sizeof(*cmd->arguments)); + if (!new) { + while (n) + free(command[--n]); + free(command); + return -1; + } + cmd->arguments = new; + memcpy(&cmd->arguments[cmd->narguments], command, (n + 1) * sizeof(*command)); + cmd->narguments += n; + free(command); + } + return 0; + } +} + + +#else +TESTED_ELSEWHERE /* libexec_construct_command.c */ +#endif diff --git a/libexec_vpipe_commands.c b/libexec_vpipe_commands.c new file mode 100644 index 0000000..6f1eb3f --- /dev/null +++ b/libexec_vpipe_commands.c @@ -0,0 +1,30 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vpipe_commands(enum libexec_pipe how, va_list args) +{ + struct libexec_command **cmds; + size_t n = 0, i; + int ret; + va_list args2; + va_copy(args2, args); + while (va_arg(args2, struct libexec_command *)) + n += 1; + va_end(args2); + cmds = calloc(n + 1, sizeof(*cmds)); + if (!cmds) + return -1; + for (i = 0; i < n; i++) + cmds[i] = va_arg(args, struct libexec_command *); + ret = libexec_pipe_commandsvn(how, cmds, n); + free(cmds); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_pipe_commands.c */ +#endif diff --git a/libexec_vputenvf.c b/libexec_vputenvf.c new file mode 100644 index 0000000..3d8b7f9 --- /dev/null +++ b/libexec_vputenvf.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + +int +libexec_vputenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *fmt, va_list args) +{ + int len, ret; + char *buf; + va_list args2; + if (!fmt) { + errno = EINVAL; + return -1; + } + va_copy(args2, args); + len = vsnprintf(NULL, 0, fmt, args2); + va_end(args2); + if (len < 0) + return -1; + buf = malloc((size_t)len + 1U); + if (!buf) + return -1; + if (vsprintf(buf, fmt, args) != len) + abort(); + ret = libexec_putenv(cmd, how, buf); + free(buf); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf.c */ +#endif diff --git a/libexec_vputenvf_append.c b/libexec_vputenvf_append.c new file mode 100644 index 0000000..ca0d564 --- /dev/null +++ b/libexec_vputenvf_append.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_append(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_APPEND, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_append.c */ +#endif diff --git a/libexec_vputenvf_noclobber.c b/libexec_vputenvf_noclobber.c new file mode 100644 index 0000000..61a1e74 --- /dev/null +++ b/libexec_vputenvf_noclobber.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_noclobber(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_NOCLOBBER, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_noclobber.c */ +#endif diff --git a/libexec_vputenvf_noreplace.c b/libexec_vputenvf_noreplace.c new file mode 100644 index 0000000..6998742 --- /dev/null +++ b/libexec_vputenvf_noreplace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_noreplace(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_NOREPLACE, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_noreplace.c */ +#endif diff --git a/libexec_vputenvf_prepend.c b/libexec_vputenvf_prepend.c new file mode 100644 index 0000000..e1b9614 --- /dev/null +++ b/libexec_vputenvf_prepend.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_prepend(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_PREPEND, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_prepend.c */ +#endif diff --git a/libexec_vputenvf_replace.c b/libexec_vputenvf_replace.c new file mode 100644 index 0000000..10c22d2 --- /dev/null +++ b/libexec_vputenvf_replace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vputenvf_replace(struct libexec_command *cmd, const char *fmt, va_list args) +{ + return libexec_vputenvf(cmd, LIBEXEC_REPLACE, fmt, args); +} + + +#else +TESTED_ELSEWHERE /* libexec_putenvf_replace.c */ +#endif diff --git a/libexec_vrun.c b/libexec_vrun.c new file mode 100644 index 0000000..99822c2 --- /dev/null +++ b/libexec_vrun.c @@ -0,0 +1,307 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +int +libexec_vrun(struct libexec_result *out, va_list args) +{ +#define CMD (cmds[ncmds - 1]) + + struct libexec_document *doc, **docs = NULL; + struct libexec_command **cmds = NULL; + size_t ncmds = 0, ndocs = 0, i; + const char *fmt, *arg_str1, *arg_str2; + char *arg_str_nc; + int arg_int1, arg_int2, arg_int3, fd, *exit_statuses = NULL; + enum libexec_insert_mode insert_mode; + void *new, *arg_voidp1; + enum libexec_pipe pipe_how = 0; + struct epoll_event ev; + int alien_epoll = -1; + const sigset_t *sigmask; + int (*on_alien_epoll)(int alien_epoll, uint32_t events, void *user); + int (*on_alien_child_death)(pid_t pid, void *user); + int (*after_fork)(struct libexec_command *cmd, int new_fd, void *user); + int (*reap_mutex_control)(int action, void *user); + int (*on_interrupt)(void *user); + void *on_alien_epoll_user = NULL; + void *on_alien_child_death_user = NULL; + void *after_fork_user = NULL; + void *reap_mutex_control_user = NULL; + void *on_interrupt_user = NULL; + int ret = -1; + + if (!out) { + errno = EINVAL; + return -1; + } + + memset(&ev, 0, sizeof(ev)); + memset(out, 0, sizeof(*out)); + out->exit_statuses = NULL; + +new_command: + new = realloc(cmds, (ncmds + 1) * sizeof(*cmds)); + if (!new) + goto fail; + cmds = new; + ncmds += 1; + CMD = malloc(sizeof(CMD)); + if (!CMD) + goto fail; + libexec_init_command(CMD); + fmt = va_arg(args, const char *); + if (libexec_vconstruct_command(CMD, fmt, args)) + goto fail; + + for (;;) { + switch (va_arg(args, enum libexec_run_instruction)) { + case LIBEXEC_RUN_END: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + goto constructed; + + case LIBEXEC_RUN_END_CIRCULAR_PIPE: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + pipe_how = va_arg(args, enum libexec_pipe); + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 1], cmds[0])) + goto fail; + goto constructed; + + case LIBEXEC_RUN_PIPE: + if (ncmds > 1) + if (libexec_pipe_commands(pipe_how, cmds[ncmds - 2], cmds[ncmds - 1])) + goto fail; + pipe_how = va_arg(args, enum libexec_pipe); + goto new_command; + + case LIBEXEC_RUN_SET_EXECUTABLE: + if (libexec_set_executable(CMD, va_arg(args, const char *))) + goto fail; + break; + + case LIBEXEC_RUN_SET_REQUIRE_PATH: + libexec_set_require_path(CMD, va_arg(args, int)); + break; + + case LIBEXEC_RUN_SET_EXEC_FD: + if (libexec_set_exec_fd(CMD, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_SET_EXEC_PATH: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + if (libexec_set_exec_path(CMD, arg_int1, arg_str1)) + goto fail; + break; + + case LIBEXEC_RUN_COPY_ENVIRON: + if (libexec_copy_environ(CMD, va_arg(args, const char *const *))) + goto fail; + break; + + case LIBEXEC_RUN_SET_ENVIRON: + libexec_set_environ(CMD, va_arg(args, char **)); + break; + + case LIBEXEC_RUN_CLEAR_ENVIRON: + if (libexec_clear_environ(CMD)) + goto fail; + break; + + case LIBEXEC_RUN_UNSETENV: + if (libexec_unsetenv(CMD, va_arg(args, const char *))) + goto fail; + break; + + case LIBEXEC_RUN_PUTENV: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + if (libexec_putenv(CMD, insert_mode, arg_str1)) + goto fail; + break; + + case LIBEXEC_RUN_SETENV: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + arg_str2 = va_arg(args, const char *); + if (libexec_setenv(CMD, insert_mode, arg_str1, arg_str2)) + goto fail; + break; + + case LIBEXEC_RUN_PUTENVF: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + if (libexec_vputenvf(CMD, insert_mode, arg_str1, args)) + goto fail; + break; + + case LIBEXEC_RUN_SETENVF: + insert_mode = va_arg(args, enum libexec_insert_mode); + arg_str1 = va_arg(args, const char *); + arg_str2 = va_arg(args, const char *); + if (libexec_vsetenvf(CMD, insert_mode, arg_str1, arg_str2, args)) + goto fail; + break; + + case LIBEXEC_RUN_OPEN: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_int2 = va_arg(args, int); + if (libexec_open(CMD, arg_int1, arg_str1, arg_int2, va_arg(args, mode_t))) + goto fail; + break; + + case LIBEXEC_RUN_OPENAT: + arg_int1 = va_arg(args, int); + arg_int2 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_int3 = va_arg(args, int); + if (libexec_openat(CMD, arg_int1, arg_int2, arg_str1, arg_int3, va_arg(args, mode_t))) + goto fail; + break; + + case LIBEXEC_RUN_OPENAT2: + arg_int1 = va_arg(args, int); + arg_int2 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + arg_voidp1 = va_arg(args, void *); + if (libexec_openat2(CMD, arg_int1, arg_int2, arg_str1, arg_voidp1, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_DUP: + arg_int1 = va_arg(args, int); + if (libexec_dup(CMD, arg_int1, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_CLOSE: + if (libexec_close(CMD, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_RENUMBER_FD: + arg_int1 = va_arg(args, int); + if (libexec_renumber_fd(CMD, arg_int1, va_arg(args, int))) + goto fail; + break; + + case LIBEXEC_RUN_INPUT_COPY: + arg_int1 = va_arg(args, int); + arg_str1 = va_arg(args, const char *); + if (libexec_input_data_copy(CMD, arg_int1, arg_str1, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_INPUT_GIFT: + arg_int1 = va_arg(args, int); + arg_str_nc = va_arg(args, char *); + if (libexec_input_data_gift(CMD, arg_int1, arg_str_nc, va_arg(args, size_t))) + goto fail; + break; + + case LIBEXEC_RUN_ADD_OUTPUT_FD: + arg_int1 = va_arg(args, int); + fd = va_arg(args, int); + if (libexec_add_output_fd(CMD, arg_int1, fd)) + goto fail; + if (alien_epoll >= 0) { + ev.events = EPOLLIN; + ev.data.fd = fd; + if (epoll_ctl(alien_epoll, EPOLL_CTL_ADD, fd, &ev)) + goto fail; + } + break; + + case LIBEXEC_RUN_ADD_OUTPUT: + arg_int1 = va_arg(args, int); + doc = va_arg(args, struct libexec_document *); + new = realloc(docs, (ndocs + 1) * sizeof(*docs)); + if (!new) + goto fail; + docs = new; + docs[ndocs++] = doc; + if (libexec_add_output_document(CMD, arg_int1, doc, O_CLOEXEC | O_NONBLOCK)) + goto fail; + break; + + case LIBEXEC_RUN_SET_SIGMASK: + sigmask = va_arg(args, const sigset_t *); + break; + + case LIBEXEC_RUN_SET_FD_CALLBACK: + on_alien_epoll = va_arg(args, int (*)(int, uint32_t, void *)); + on_alien_epoll_user = va_arg(args, void *); + alien_epoll = epoll_create1(EPOLL_CLOEXEC); + if (alien_epoll < 0) + goto fail; + break; + + case LIBEXEC_RUN_SET_REAPER: + on_alien_child_death = va_arg(args, int (*)(pid_t, void *)); + on_alien_child_death_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_ATFORK: + after_fork = va_arg(args, int (*)(struct libexec_command *, int, void *)); + after_fork_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_MUTEX: + reap_mutex_control = va_arg(args, int (*)(int, void *)); + reap_mutex_control_user = va_arg(args, void *); + break; + + case LIBEXEC_RUN_SET_INTERRUPT_CALLBACK: + on_interrupt = va_arg(args, int (*)(void *)); + on_interrupt_user = va_arg(args, void *); + break; + + default: + errno = EINVAL; + goto fail; + } + } + +constructed: + ret = libexec_run_pipeline(on_alien_epoll, alien_epoll, on_alien_epoll_user, + on_alien_child_death, on_alien_child_death_user, + after_fork, after_fork_user, + reap_mutex_control, reap_mutex_control_user, + on_interrupt, on_interrupt_user, + sigmask, docs, ndocs, 0, cmds, exit_statuses, ncmds); + if (!ret) { + out->exit_statuses = exit_statuses; + out->proc_count = ncmds; + for (i = 0; i < ncmds; i++) + if (exit_statuses[i]) + break; + out->all_successful = (i == ncmds); + exit_statuses = NULL; + } + +fail: + while (ncmds--) { + if (cmds[ncmds]) { + libexec_destroy_command(cmds[ncmds]); + free(cmds[ncmds]); + } + } + free(cmds); + free(docs); + if (alien_epoll >= 0) + close(alien_epoll); + free(exit_statuses); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_run.c */ +#endif diff --git a/libexec_vsetenvf.c b/libexec_vsetenvf.c new file mode 100644 index 0000000..b4d35c5 --- /dev/null +++ b/libexec_vsetenvf.c @@ -0,0 +1,39 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wformat-nonliteral" +#endif + + +int +libexec_vsetenvf(struct libexec_command *cmd, enum libexec_insert_mode how, const char *name, const char *value_fmt, va_list args) +{ + int len, ret; + char *buf; + va_list args2; + if (!name || !value_fmt) { + errno = EINVAL; + return -1; + } + va_copy(args2, args); + len = vsnprintf(NULL, 0, value_fmt, args2); + va_end(args2); + if (len < 0) + return -1; + buf = malloc((size_t)len + 1U); + if (!buf) + return -1; + if (vsprintf(buf, value_fmt, args) != len) + abort(); + ret = libexec_setenv(cmd, how, name, buf); + free(buf); + return ret; +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf.c */ +#endif diff --git a/libexec_vsetenvf_append.c b/libexec_vsetenvf_append.c new file mode 100644 index 0000000..c00f8b6 --- /dev/null +++ b/libexec_vsetenvf_append.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_append.c */ +#endif diff --git a/libexec_vsetenvf_noclobber.c b/libexec_vsetenvf_noclobber.c new file mode 100644 index 0000000..5164e8b --- /dev/null +++ b/libexec_vsetenvf_noclobber.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_noclobber.c */ +#endif diff --git a/libexec_vsetenvf_noreplace.c b/libexec_vsetenvf_noreplace.c new file mode 100644 index 0000000..1fb1476 --- /dev/null +++ b/libexec_vsetenvf_noreplace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_noreplace.c */ +#endif diff --git a/libexec_vsetenvf_prepend.c b/libexec_vsetenvf_prepend.c new file mode 100644 index 0000000..63daaae --- /dev/null +++ b/libexec_vsetenvf_prepend.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_prepend.c */ +#endif diff --git a/libexec_vsetenvf_replace.c b/libexec_vsetenvf_replace.c new file mode 100644 index 0000000..d1e8788 --- /dev/null +++ b/libexec_vsetenvf_replace.c @@ -0,0 +1,15 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" +#ifndef TEST + + +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); +} + + +#else +TESTED_ELSEWHERE /* libexec_setenvf_replace.c */ +#endif diff --git a/mk/linux.mk b/mk/linux.mk new file mode 100644 index 0000000..ad58f69 --- /dev/null +++ b/mk/linux.mk @@ -0,0 +1,6 @@ +LIBEXT = so +LIBFLAGS = -shared -Wl,-soname,lib$(LIB_NAME).$(LIBEXT).$(LIB_MAJOR) +LIBMAJOREXT = $(LIBEXT).$(LIB_MAJOR) +LIBMINOREXT = $(LIBEXT).$(LIB_VERSION) + +FIX_INSTALL_NAME = : diff --git a/mk/macos.mk b/mk/macos.mk new file mode 100644 index 0000000..55d3eaa --- /dev/null +++ b/mk/macos.mk @@ -0,0 +1,6 @@ +LIBEXT = dylib +LIBFLAGS = -dynamiclib -Wl,-compatibility_version,$(LIB_MAJOR) -Wl,-current_version,$(LIB_VERSION) +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = install_name_tool -id "$(PREFIX)/lib/libexec.$(LIBMAJOREXT)" diff --git a/mk/windows.mk b/mk/windows.mk new file mode 100644 index 0000000..ed5ec8d --- /dev/null +++ b/mk/windows.mk @@ -0,0 +1,6 @@ +LIBEXT = dll +LIBFLAGS = -shared +LIBMAJOREXT = $(LIB_MAJOR).$(LIBEXT) +LIBMINOREXT = $(LIB_VERSION).$(LIBEXT) + +FIX_INSTALL_NAME = : diff --git a/testhelp.c b/testhelp.c new file mode 100644 index 0000000..74997eb --- /dev/null +++ b/testhelp.c @@ -0,0 +1,366 @@ +/* See LICENSE file for copyright and license details. */ +#define TEST +#include "common.h" + + +#if defined(__GNUC__) +# pragma GCC diagnostic ignored "-Wswitch" +# pragma GCC diagnostic ignored "-Wswitch-enum" +#endif + + +static char * +quote(const char *s, char **out) +{ + char *ret, *p; + int safe = 1; + *out = p = ret = malloc(strlen(s) * 4 + 3); + if (!ret) { + fprintf(stderr, "ran out of memory during printing of assertion failure\n"); + exit(1); + } + *p++ = '"'; + for (; *s; s++) { + if (*s == '\r') + p = stpcpy(p, "\\r"), safe = 1; + else if (*s == '\t') + p = stpcpy(p, "\\t"), safe = 1; + else if (*s == '\a') + p = stpcpy(p, "\\a"), safe = 1; + else if (*s == '\f') + p = stpcpy(p, "\\f"), safe = 1; + else if (*s == '\v') + p = stpcpy(p, "\\v"), safe = 1; + else if (*s == '\b') + p = stpcpy(p, "\\b"), safe = 1; + else if (*s == '\n') + p = stpcpy(p, "\\n"), safe = 1; + else if (*s == '\"') + p = stpcpy(p, "\\\""), safe = 1; + else if (*s == '\\') + p = stpcpy(p, "\\\\"), safe = 1; + else if (*s < ' ' || *s >= 127) + p += sprintf(p, "\\x%02X", (unsigned char)*s), safe = 0; + else if (*s == '?' && p[-1] == '?') + p += sprintf(p, "\"\"?"), safe = 1; + else if (isxdigit(*s) && !safe) + p += sprintf(p, "\"\"%c", *s), safe = 1; + else + *p++ = *s, safe = 1; + } + *p++ = '"'; + *p++ = '\0'; + return ret; +} + + +#define CASE(HOW, OP)\ + case HOW:\ + if ((*have) OP (*expect))\ + return;\ + symbol = #OP;\ + break + + +#define CASE_LOGIC(HOW, OP1, OP2, OPERAND3_IS_EXPECT)\ + case HOW:\ + if (((*have) OP1 (*expect)) OP2 (OPERAND3_IS_EXPECT ? (*expect) : 0))\ + return;\ + symbol = #OP1;\ + symbol2 = #OP2;\ + operand3_is_expect = (OPERAND3_IS_EXPECT);\ + break + + +static void +test_assert_uint(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const uintmax_t *have, const uintmax_t *expect) +{ + const char *symbol; + const char *symbol2 = NULL; + int operand3_is_expect; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + CASE_LOGIC(ASSERT_CONTAINS_ALL, &, ==, 1); + CASE_LOGIC(ASSERT_CONTAINS_ANY, &, !=, 0); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ALL, &, !=, 1); + CASE_LOGIC(ASSERT_CONTAINS_NOT_ANY, &, ==, 0); + CASE_LOGIC(ASSERT_IS_IN, &~, ==, 0); + CASE_LOGIC(ASSERT_NOT_IN, &~, !=, 0); + default: + abort(); + } + if (!symbol2) + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ju %s %ju\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + else if (!operand3_is_expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s 0, actualised as (%#jx %s %#jx) %s 0\n", + file, line, have_string, symbol, expect_string, symbol2, *have, symbol, *expect, symbol2); + else + fprintf(stderr, "TEST FAILED at %s:%i: asserting ((%s) %s (%s)) %s (%s), actualised as (%#jx %s %#jx) %s %#jx\n", + file, line, have_string, symbol, expect_string, symbol2, expect_string, + *have, symbol, *expect, symbol2, *expect); + exit(1); +} + + +static void +test_assert_int(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const intmax_t *have, const intmax_t *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + CASE(ASSERT_EQ, ==); + CASE(ASSERT_NE, !=); + CASE(ASSERT_LT, <); + CASE(ASSERT_LE, <=); + CASE(ASSERT_GT, >); + CASE(ASSERT_GE, >=); + default: + test_assert_uint(file, line, how, have_string, expect_string, + (const uintmax_t *)have, (const uintmax_t *)expect); + return; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %ji %s %ji\n", + file, line, have_string, symbol, expect_string, *have, symbol, *expect); + exit(1); +} + + +#define PTR_CASE(HOW, OP)\ + case HOW:\ + if ((have) OP (expect))\ + return;\ + symbol = #OP;\ + break + + +static void +test_assert_ptr(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect) +{ + const char *symbol; + switch (how) { + case ASSERT_NOT_LT: how = ASSERT_NOT_GE; break; + case ASSERT_NOT_LE: how = ASSERT_NOT_GT; break; + case ASSERT_NOT_GT: how = ASSERT_NOT_LE; break; + case ASSERT_NOT_GE: how = ASSERT_NOT_LT; break; + } + switch (how) { + PTR_CASE(ASSERT_EQ, ==); + PTR_CASE(ASSERT_NE, !=); + PTR_CASE(ASSERT_LT, <); + PTR_CASE(ASSERT_LE, <=); + PTR_CASE(ASSERT_GT, >); + PTR_CASE(ASSERT_GE, >=); + default: + abort(); + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting (%s) %s (%s), actualised as %p %s %p\n", + file, line, have_string, symbol, expect_string, have, symbol, expect); + exit(1); +} + + +#define STR_CASE(HOW, FUNC)\ + case HOW:\ + if (FUNC(have, expect))\ + return;\ + function = #FUNC;\ + break + +#define SWAP_STR_CASE(HOW, NEW_HOW)\ + case HOW:\ + test_assert_str(file, line, how, expect_string, have_string, expect, have, rev);\ + return + + +static void +test_assert_str(const char *file, int line, enum assert_how how, const char *have_string, + const char *expect_string, const char *have, const char *expect, int rev) +{ + const char *function; + size_t have_len; + size_t expect_len; + char *have_quote = NULL; + char *expect_quote = NULL; + + if (!have && !expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) and (%s) are NULL\n", + file, line, have_string, expect_string); + else if (!have) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, have_string); + else if (!expect) + fprintf(stderr, "TEST FAILED at %s:%i: asserting failed because (%s) was NULL\n", + file, line, expect_string); + if (!have || !expect) + exit(1); + + switch (how) { + STR_CASE(ASSERT_EQ, !strcmp); + STR_CASE(ASSERT_NE, strcmp); + STR_CASE(ASSERT_IS_IN, strstr); + STR_CASE(ASSERT_NOT_IN, !strstr); + SWAP_STR_CASE(ASSERT_CONTAINS_ALL, ASSERT_IS_IN); + SWAP_STR_CASE(ASSERT_CONTAINS_NOT_ALL, ASSERT_NOT_INT); + SWAP_STR_CASE(ASSERT_LE, ASSERT_GE); + SWAP_STR_CASE(ASSERT_LT, ASSERT_GT); + SWAP_STR_CASE(ASSERT_NOT_LE, ASSERT_NOT_GE); + SWAP_STR_CASE(ASSERT_NOT_LT, ASSERT_NOT_GT); + default: + have_len = strlen(have); + expect_len = strlen(expect); + if (rev) + goto assert_end; + else + goto assert_start; + } + fprintf(stderr, "TEST FAILED at %s:%i: asserting %s(%s, %s), actualised as %s(%s, %s)\n", + file, line, function, have_string, expect_string, + function, quote(have, &have_quote), quote(expect, &expect_quote)); +free_and_exit: + free(have_quote); + free(expect_quote); + exit(1); + +assert_start: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu >= %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GE: + if (have_len < expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu < %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_GT: + if (have_len > expect_len && !strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strncmp(%s, %s, strlen(%s)), " + "actualised as %zu > %zu && !strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strncmp(have, expect, expect_len)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strncmp(%s, %s, strlen(%s)), " + "actualised as %zu <= %zu || strncmp(%s, %s, %zu)\n", + file, line, have_string, expect_string, have_string, expect_string, expect_string, + have_len, expect_len, quote(have, &have_quote), quote(expect, &expect_quote), expect_len); + break; + default: + abort(); + } + goto free_and_exit; + +assert_end: + switch (how) { + case ASSERT_GE: + if (have_len >= expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) >= strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu >= %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GE: + if (have_len > expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) < strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu < %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_GT: + if (have_len > expect_len && !strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) > strlen(%s) && !strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu > %zu && !strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + case ASSERT_NOT_GT: + if (have_len <= expect_len || strcmp(&have[have_len - expect_len], expect)) + return; + fprintf(stderr, "TEST FAILED at %s:%i: " + "asserting strlen(%s) <= strlen(%s) || strcmp(&(%s)[strlen(%s) - strlen(%s)], %s), " + "actualised as %zu <= %zu || strcmp(&%s[%ti], %s)\n", + file, line, have_string, expect_string, have_string, have_string, expect_string, + expect_string, have_len, expect_len, quote(have, &have_quote), + (ptrdiff_t)have_len - (ptrdiff_t)expect_len, quote(expect, &expect_quote)); + break; + default: + abort(); + } + goto free_and_exit; +} + + +void +test_assert(const char *file, int line, enum assert_type type, enum assert_how how, + const char *have_string, const char *expect_string, const void *have, const void *expect) +{ + switch (type) { + case ASSERT_ENUM: + case ASSERT_UINT: + test_assert_uint(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_INT: + test_assert_int(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_NULL: + case ASSERT_PTR: + test_assert_ptr(file, line, how, have_string, expect_string, have, expect); + break; + + case ASSERT_STR: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 0); + break; + + case ASSERT_STR_REV: + test_assert_str(file, line, how, have_string, expect_string, have, expect, 1); + break; + + default: + abort(); + } +} |