brintos

brintos / linux-shallow public Read only

0
0
Text · 6.9 KiB · ed114b0 Raw
289 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2#include <dirent.h>3#include <errno.h>4#include <fcntl.h>5#include <linux/ctype.h>6#include <linux/kernel.h>7#include <linux/string.h>8#include <linux/zalloc.h>9#include <string.h>10#include <stdlib.h>11#include <sys/types.h>12#include <unistd.h>13#include <subcmd/exec-cmd.h>14#include <subcmd/parse-options.h>15#include <sys/wait.h>16#include <sys/stat.h>17#include <api/io.h>18#include "builtin.h"19#include "tests-scripts.h"20#include "color.h"21#include "debug.h"22#include "hist.h"23#include "intlist.h"24#include "string2.h"25#include "symbol.h"26#include "tests.h"27#include "util/rlimit.h"28#include "util/util.h"29 30static int shell_tests__dir_fd(void)31{32	struct stat st;33	char path[PATH_MAX], path2[PATH_MAX], *exec_path;34	static const char * const devel_dirs[] = {35		"./tools/perf/tests/shell",36		"./tests/shell",37		"./source/tests/shell"38	};39	int fd;40	char *p;41 42	for (size_t i = 0; i < ARRAY_SIZE(devel_dirs); ++i) {43		fd = open(devel_dirs[i], O_PATH);44 45		if (fd >= 0)46			return fd;47	}48 49	/* Use directory of executable */50	if (readlink("/proc/self/exe", path2, sizeof path2) < 0)51		return -1;52	/* Follow another level of symlink if there */53	if (lstat(path2, &st) == 0 && (st.st_mode & S_IFMT) == S_IFLNK) {54		scnprintf(path, sizeof(path), path2);55		if (readlink(path, path2, sizeof path2) < 0)56			return -1;57	}58	/* Get directory */59	p = strrchr(path2, '/');60	if (p)61		*p = 0;62	scnprintf(path, sizeof(path), "%s/tests/shell", path2);63	fd = open(path, O_PATH);64	if (fd >= 0)65		return fd;66	scnprintf(path, sizeof(path), "%s/source/tests/shell", path2);67	fd = open(path, O_PATH);68	if (fd >= 0)69		return fd;70 71	/* Then installed path. */72	exec_path = get_argv_exec_path();73	scnprintf(path, sizeof(path), "%s/tests/shell", exec_path);74	free(exec_path);75	return open(path, O_PATH);76}77 78static char *shell_test__description(int dir_fd, const char *name)79{80	struct io io;81	char buf[128], desc[256];82	int ch, pos = 0;83 84	io__init(&io, openat(dir_fd, name, O_RDONLY), buf, sizeof(buf));85	if (io.fd < 0)86		return NULL;87 88	/* Skip first line - should be #!/bin/sh Shebang */89	if (io__get_char(&io) != '#')90		goto err_out;91	if (io__get_char(&io) != '!')92		goto err_out;93	do {94		ch = io__get_char(&io);95		if (ch < 0)96			goto err_out;97	} while (ch != '\n');98 99	do {100		ch = io__get_char(&io);101		if (ch < 0)102			goto err_out;103	} while (ch == '#' || isspace(ch));104	while (ch > 0 && ch != '\n') {105		desc[pos++] = ch;106		if (pos >= (int)sizeof(desc) - 1)107			break;108		ch = io__get_char(&io);109	}110	while (pos > 0 && isspace(desc[--pos]))111		;112	desc[++pos] = '\0';113	close(io.fd);114	return strdup(desc);115err_out:116	close(io.fd);117	return NULL;118}119 120/* Is this full file path a shell script */121static bool is_shell_script(int dir_fd, const char *path)122{123	const char *ext;124 125	ext = strrchr(path, '.');126	if (!ext)127		return false;128	if (!strcmp(ext, ".sh")) { /* Has .sh extension */129		if (faccessat(dir_fd, path, R_OK | X_OK, 0) == 0) /* Is executable */130			return true;131	}132	return false;133}134 135/* Is this file in this dir a shell script (for test purposes) */136static bool is_test_script(int dir_fd, const char *name)137{138	return is_shell_script(dir_fd, name);139}140 141/* Duplicate a string and fall over and die if we run out of memory */142static char *strdup_check(const char *str)143{144	char *newstr;145 146	newstr = strdup(str);147	if (!newstr) {148		pr_err("Out of memory while duplicating test script string\n");149		abort();150	}151	return newstr;152}153 154static int shell_test__run(struct test_suite *test, int subtest __maybe_unused)155{156	const char *file = test->priv;157	int err;158	char *cmd = NULL;159 160	if (asprintf(&cmd, "%s%s", file, verbose ? " -v" : "") < 0)161		return TEST_FAIL;162	err = system(cmd);163	free(cmd);164	if (!err)165		return TEST_OK;166 167	return WEXITSTATUS(err) == 2 ? TEST_SKIP : TEST_FAIL;168}169 170static void append_script(int dir_fd, const char *name, char *desc,171			  struct test_suite ***result,172			  size_t *result_sz)173{174	char filename[PATH_MAX], link[128];175	struct test_suite *test_suite, **result_tmp;176	struct test_case *tests;177	size_t len;178 179	snprintf(link, sizeof(link), "/proc/%d/fd/%d", getpid(), dir_fd);180	len = readlink(link, filename, sizeof(filename));181	if (len < 0) {182		pr_err("Failed to readlink %s", link);183		return;184	}185	filename[len++] = '/';186	strcpy(&filename[len], name);187 188	tests = calloc(2, sizeof(*tests));189	if (!tests) {190		pr_err("Out of memory while building script test suite list\n");191		return;192	}193	tests[0].name = strdup_check(name);194	tests[0].desc = strdup_check(desc);195	tests[0].run_case = shell_test__run;196 197	test_suite = zalloc(sizeof(*test_suite));198	if (!test_suite) {199		pr_err("Out of memory while building script test suite list\n");200		free(tests);201		return;202	}203	test_suite->desc = desc;204	test_suite->test_cases = tests;205	test_suite->priv = strdup_check(filename);206	/* Realloc is good enough, though we could realloc by chunks, not that207	 * anyone will ever measure performance here */208	result_tmp = realloc(*result, (*result_sz + 1) * sizeof(*result_tmp));209	if (result_tmp == NULL) {210		pr_err("Out of memory while building script test suite list\n");211		free(tests);212		free(test_suite);213		return;214	}215	/* Add file to end and NULL terminate the struct array */216	*result = result_tmp;217	(*result)[*result_sz] = test_suite;218	(*result_sz)++;219}220 221static void append_scripts_in_dir(int dir_fd,222				  struct test_suite ***result,223				  size_t *result_sz)224{225	struct dirent **entlist;226	struct dirent *ent;227	int n_dirs, i;228 229	/* List files, sorted by alpha */230	n_dirs = scandirat(dir_fd, ".", &entlist, NULL, alphasort);231	if (n_dirs == -1)232		return;233	for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {234		int fd;235 236		if (ent->d_name[0] == '.')237			continue; /* Skip hidden files */238		if (is_test_script(dir_fd, ent->d_name)) { /* It's a test */239			char *desc = shell_test__description(dir_fd, ent->d_name);240 241			if (desc) /* It has a desc line - valid script */242				append_script(dir_fd, ent->d_name, desc, result, result_sz);243			continue;244		}245		if (ent->d_type != DT_DIR) {246			struct stat st;247 248			if (ent->d_type != DT_UNKNOWN)249				continue;250			fstatat(dir_fd, ent->d_name, &st, 0);251			if (!S_ISDIR(st.st_mode))252				continue;253		}254		if (strncmp(ent->d_name, "base_", 5) == 0)255			continue; /* Skip scripts that have a separate driver. */256		fd = openat(dir_fd, ent->d_name, O_PATH);257		append_scripts_in_dir(fd, result, result_sz);258	}259	for (i = 0; i < n_dirs; i++) /* Clean up */260		zfree(&entlist[i]);261	free(entlist);262}263 264struct test_suite **create_script_test_suites(void)265{266	struct test_suite **result = NULL, **result_tmp;267	size_t result_sz = 0;268	int dir_fd = shell_tests__dir_fd(); /* Walk  dir */269 270	/*271	 * Append scripts if fd is good, otherwise return a NULL terminated zero272	 * length array.273	 */274	if (dir_fd >= 0)275		append_scripts_in_dir(dir_fd, &result, &result_sz);276 277	result_tmp = realloc(result, (result_sz + 1) * sizeof(*result_tmp));278	if (result_tmp == NULL) {279		pr_err("Out of memory while building script test suite list\n");280		abort();281	}282	/* NULL terminate the test suite array. */283	result = result_tmp;284	result[result_sz] = NULL;285	if (dir_fd >= 0)286		close(dir_fd);287	return result;288}289