From b228561897aecea8a60908c354cee79938cf4e31 Mon Sep 17 00:00:00 2001 From: Richard Maw Date: Wed, 24 Sep 2014 16:24:48 +0000 Subject: Add test shell for use in staging area tests This is intended to be statically linked, so it can be run in the staging area, without having to build a libc. You shouldn't generally statically link GLibc, because it dynamically links things like NSS modules or locale data, but we only need some very simple stuff, so we can get away with that. There's also potential licensing issues, as GLibc is LGPLv2, so distribution requires providing the ability to re-link against another library, but its use in the test-suite shouldn't count as distribution. There's a couple of commands, the only two needed by the yarn test suite are "copy files" which is like `cp -r . "$DESTDIR"`, and "false", which is for commands to deliberately fail. --- scripts/test-shell.c | 144 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 scripts/test-shell.c (limited to 'scripts/test-shell.c') diff --git a/scripts/test-shell.c b/scripts/test-shell.c new file mode 100644 index 00000000..7975c188 --- /dev/null +++ b/scripts/test-shell.c @@ -0,0 +1,144 @@ +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include + +char *readlinka(char const *path){ + size_t buflen = BUFSIZ; + char *buf = malloc(buflen); + ssize_t read; + while ((read = readlink(path, buf, buflen - 1)) >= buflen - 1) { + char *newbuf = realloc(buf, buflen * 2); + if (newbuf == NULL) { + goto failure; + } + buf = newbuf; + buflen = buflen * 2; + } + buf[read] = '\0'; + return buf; +failure: + free(buf); + return NULL; +} + +int copy_file_paths(char const *source_file, char const *target_file) { + int source_fd; + int target_fd; + int ret = -1; + struct stat st; + if ((source_fd = open(source_file, O_RDONLY)) == -1) { + return ret; + } + if (fstat(source_fd, &st) == -1) { + perror("stat"); + ret = -2; + goto cleanup_in; + } + if ((target_fd = open(target_file, O_WRONLY|O_CREAT, st.st_mode)) == -1) { + ret = -3; + goto cleanup_in; + } + ssize_t read; + while ((read = sendfile(target_fd, source_fd, NULL, BUFSIZ)) > 0); + if (read < 0) { + perror("sendfile"); + ret = -4; + } + ret = 0; +cleanup_all: + close(target_fd); +cleanup_in: + close(source_fd); + return ret; +} + +int copy_entry(const char *fpath, const struct stat *sb, int typeflag, + struct FTW *ftwbuf) { + int ret = 0; + char *target_path = NULL; + if (asprintf(&target_path, "%s/%s", getenv("DESTDIR"), fpath) == -1) { + return -1; + } + switch (typeflag) { + case FTW_F: + /* Copy file */ + if ((ret = copy_file_paths(fpath, target_path)) < 0) { + perror("Copy file"); + ret = -1; + } + break; + case FTW_D: + case FTW_DNR: + /* Copy directory */ + if (mkdir(target_path, sb->st_mode)) { + if (errno != EEXIST) { + perror("mkdir"); + ret = -1; + } + } + break; + case FTW_NS: + case FTW_SL: + case FTW_SLN: { + /* Create symlink */ + char *link_target = readlinka(fpath); + if (link_target == NULL) { + perror("readlink"); + ret = -1; + } + if (symlink(link_target, target_path) == -1) { + perror("symlink"); + ret = -1; + } + break; + } + } +cleanup: + free(target_path); + return ret; +} + +int main(int argc, char *argv[]) { + int ret = 1; + if (argc != 3 || strcmp(argv[1], "-c") != 0) { + fprintf(stderr, "Usage: %s -c COMMAND\n", argv[0]); + return 1; + } + size_t cmdlen = strlen(argv[2]); + FILE *cmdstream = fmemopen(argv[2], cmdlen, "r"); + { + ssize_t read; + size_t len = 0; + char *line = NULL; + + ret = 0; + while ((read = getline(&line, &len, cmdstream)) != -1) { + if (line[read - 1] == '\n') line[read - 1] = '\0'; + if (strcmp(line, "copy files") == 0) { + /* Recursively copy contents of current dir to DESTDIR */ + if (nftw(".", copy_entry, 20, FTW_PHYS)) { + ret = 1; + break; + } + } else if (strcmp(line, "false") == 0 || + strstr(line, "false ") == line) { + ret = 1; + break; + } else { + ret = 127; + break; + } + } + free(line); + } + return ret; +} -- cgit v1.2.1