1*7391db64SCarsten Haitzler /* SPDX-License-Identifier: GPL-2.0 */
2*7391db64SCarsten Haitzler #include <dirent.h>
3*7391db64SCarsten Haitzler #include <errno.h>
4*7391db64SCarsten Haitzler #include <fcntl.h>
5*7391db64SCarsten Haitzler #include <linux/ctype.h>
6*7391db64SCarsten Haitzler #include <linux/kernel.h>
7*7391db64SCarsten Haitzler #include <linux/string.h>
8*7391db64SCarsten Haitzler #include <linux/zalloc.h>
9*7391db64SCarsten Haitzler #include <string.h>
10*7391db64SCarsten Haitzler #include <stdlib.h>
11*7391db64SCarsten Haitzler #include <sys/types.h>
12*7391db64SCarsten Haitzler #include <unistd.h>
13*7391db64SCarsten Haitzler #include <subcmd/exec-cmd.h>
14*7391db64SCarsten Haitzler #include <subcmd/parse-options.h>
15*7391db64SCarsten Haitzler #include <sys/wait.h>
16*7391db64SCarsten Haitzler #include <sys/stat.h>
17*7391db64SCarsten Haitzler #include "builtin.h"
18*7391db64SCarsten Haitzler #include "builtin-test-list.h"
19*7391db64SCarsten Haitzler #include "color.h"
20*7391db64SCarsten Haitzler #include "debug.h"
21*7391db64SCarsten Haitzler #include "hist.h"
22*7391db64SCarsten Haitzler #include "intlist.h"
23*7391db64SCarsten Haitzler #include "string2.h"
24*7391db64SCarsten Haitzler #include "symbol.h"
25*7391db64SCarsten Haitzler #include "tests.h"
26*7391db64SCarsten Haitzler #include "util/rlimit.h"
27*7391db64SCarsten Haitzler
28*7391db64SCarsten Haitzler
29*7391db64SCarsten Haitzler /*
30*7391db64SCarsten Haitzler * As this is a singleton built once for the run of the process, there is
31*7391db64SCarsten Haitzler * no value in trying to free it and just let it stay around until process
32*7391db64SCarsten Haitzler * exits when it's cleaned up.
33*7391db64SCarsten Haitzler */
34*7391db64SCarsten Haitzler static size_t files_num = 0;
35*7391db64SCarsten Haitzler static struct script_file *files = NULL;
36*7391db64SCarsten Haitzler static int files_max_width = 0;
37*7391db64SCarsten Haitzler
shell_tests__dir(char * path,size_t size)38*7391db64SCarsten Haitzler static const char *shell_tests__dir(char *path, size_t size)
39*7391db64SCarsten Haitzler {
40*7391db64SCarsten Haitzler const char *devel_dirs[] = { "./tools/perf/tests", "./tests", };
41*7391db64SCarsten Haitzler char *exec_path;
42*7391db64SCarsten Haitzler unsigned int i;
43*7391db64SCarsten Haitzler
44*7391db64SCarsten Haitzler for (i = 0; i < ARRAY_SIZE(devel_dirs); ++i) {
45*7391db64SCarsten Haitzler struct stat st;
46*7391db64SCarsten Haitzler
47*7391db64SCarsten Haitzler if (!lstat(devel_dirs[i], &st)) {
48*7391db64SCarsten Haitzler scnprintf(path, size, "%s/shell", devel_dirs[i]);
49*7391db64SCarsten Haitzler if (!lstat(devel_dirs[i], &st))
50*7391db64SCarsten Haitzler return path;
51*7391db64SCarsten Haitzler }
52*7391db64SCarsten Haitzler }
53*7391db64SCarsten Haitzler
54*7391db64SCarsten Haitzler /* Then installed path. */
55*7391db64SCarsten Haitzler exec_path = get_argv_exec_path();
56*7391db64SCarsten Haitzler scnprintf(path, size, "%s/tests/shell", exec_path);
57*7391db64SCarsten Haitzler free(exec_path);
58*7391db64SCarsten Haitzler return path;
59*7391db64SCarsten Haitzler }
60*7391db64SCarsten Haitzler
shell_test__description(char * description,size_t size,const char * path,const char * name)61*7391db64SCarsten Haitzler static const char *shell_test__description(char *description, size_t size,
62*7391db64SCarsten Haitzler const char *path, const char *name)
63*7391db64SCarsten Haitzler {
64*7391db64SCarsten Haitzler FILE *fp;
65*7391db64SCarsten Haitzler char filename[PATH_MAX];
66*7391db64SCarsten Haitzler int ch;
67*7391db64SCarsten Haitzler
68*7391db64SCarsten Haitzler path__join(filename, sizeof(filename), path, name);
69*7391db64SCarsten Haitzler fp = fopen(filename, "r");
70*7391db64SCarsten Haitzler if (!fp)
71*7391db64SCarsten Haitzler return NULL;
72*7391db64SCarsten Haitzler
73*7391db64SCarsten Haitzler /* Skip first line - should be #!/bin/sh Shebang */
74*7391db64SCarsten Haitzler do {
75*7391db64SCarsten Haitzler ch = fgetc(fp);
76*7391db64SCarsten Haitzler } while (ch != EOF && ch != '\n');
77*7391db64SCarsten Haitzler
78*7391db64SCarsten Haitzler description = fgets(description, size, fp);
79*7391db64SCarsten Haitzler fclose(fp);
80*7391db64SCarsten Haitzler
81*7391db64SCarsten Haitzler /* Assume first char on line is omment everything after that desc */
82*7391db64SCarsten Haitzler return description ? strim(description + 1) : NULL;
83*7391db64SCarsten Haitzler }
84*7391db64SCarsten Haitzler
85*7391db64SCarsten Haitzler /* Is this full file path a shell script */
is_shell_script(const char * path)86*7391db64SCarsten Haitzler static bool is_shell_script(const char *path)
87*7391db64SCarsten Haitzler {
88*7391db64SCarsten Haitzler const char *ext;
89*7391db64SCarsten Haitzler
90*7391db64SCarsten Haitzler ext = strrchr(path, '.');
91*7391db64SCarsten Haitzler if (!ext)
92*7391db64SCarsten Haitzler return false;
93*7391db64SCarsten Haitzler if (!strcmp(ext, ".sh")) { /* Has .sh extension */
94*7391db64SCarsten Haitzler if (access(path, R_OK | X_OK) == 0) /* Is executable */
95*7391db64SCarsten Haitzler return true;
96*7391db64SCarsten Haitzler }
97*7391db64SCarsten Haitzler return false;
98*7391db64SCarsten Haitzler }
99*7391db64SCarsten Haitzler
100*7391db64SCarsten Haitzler /* Is this file in this dir a shell script (for test purposes) */
is_test_script(const char * path,const char * name)101*7391db64SCarsten Haitzler static bool is_test_script(const char *path, const char *name)
102*7391db64SCarsten Haitzler {
103*7391db64SCarsten Haitzler char filename[PATH_MAX];
104*7391db64SCarsten Haitzler
105*7391db64SCarsten Haitzler path__join(filename, sizeof(filename), path, name);
106*7391db64SCarsten Haitzler if (!is_shell_script(filename)) return false;
107*7391db64SCarsten Haitzler return true;
108*7391db64SCarsten Haitzler }
109*7391db64SCarsten Haitzler
110*7391db64SCarsten Haitzler /* Duplicate a string and fall over and die if we run out of memory */
strdup_check(const char * str)111*7391db64SCarsten Haitzler static char *strdup_check(const char *str)
112*7391db64SCarsten Haitzler {
113*7391db64SCarsten Haitzler char *newstr;
114*7391db64SCarsten Haitzler
115*7391db64SCarsten Haitzler newstr = strdup(str);
116*7391db64SCarsten Haitzler if (!newstr) {
117*7391db64SCarsten Haitzler pr_err("Out of memory while duplicating test script string\n");
118*7391db64SCarsten Haitzler abort();
119*7391db64SCarsten Haitzler }
120*7391db64SCarsten Haitzler return newstr;
121*7391db64SCarsten Haitzler }
122*7391db64SCarsten Haitzler
append_script(const char * dir,const char * file,const char * desc)123*7391db64SCarsten Haitzler static void append_script(const char *dir, const char *file, const char *desc)
124*7391db64SCarsten Haitzler {
125*7391db64SCarsten Haitzler struct script_file *files_tmp;
126*7391db64SCarsten Haitzler size_t files_num_tmp;
127*7391db64SCarsten Haitzler int width;
128*7391db64SCarsten Haitzler
129*7391db64SCarsten Haitzler files_num_tmp = files_num + 1;
130*7391db64SCarsten Haitzler if (files_num_tmp >= SIZE_MAX) {
131*7391db64SCarsten Haitzler pr_err("Too many script files\n");
132*7391db64SCarsten Haitzler abort();
133*7391db64SCarsten Haitzler }
134*7391db64SCarsten Haitzler /* Realloc is good enough, though we could realloc by chunks, not that
135*7391db64SCarsten Haitzler * anyone will ever measure performance here */
136*7391db64SCarsten Haitzler files_tmp = realloc(files,
137*7391db64SCarsten Haitzler (files_num_tmp + 1) * sizeof(struct script_file));
138*7391db64SCarsten Haitzler if (files_tmp == NULL) {
139*7391db64SCarsten Haitzler pr_err("Out of memory while building test list\n");
140*7391db64SCarsten Haitzler abort();
141*7391db64SCarsten Haitzler }
142*7391db64SCarsten Haitzler /* Add file to end and NULL terminate the struct array */
143*7391db64SCarsten Haitzler files = files_tmp;
144*7391db64SCarsten Haitzler files_num = files_num_tmp;
145*7391db64SCarsten Haitzler files[files_num - 1].dir = strdup_check(dir);
146*7391db64SCarsten Haitzler files[files_num - 1].file = strdup_check(file);
147*7391db64SCarsten Haitzler files[files_num - 1].desc = strdup_check(desc);
148*7391db64SCarsten Haitzler files[files_num].dir = NULL;
149*7391db64SCarsten Haitzler files[files_num].file = NULL;
150*7391db64SCarsten Haitzler files[files_num].desc = NULL;
151*7391db64SCarsten Haitzler
152*7391db64SCarsten Haitzler width = strlen(desc); /* Track max width of desc */
153*7391db64SCarsten Haitzler if (width > files_max_width)
154*7391db64SCarsten Haitzler files_max_width = width;
155*7391db64SCarsten Haitzler }
156*7391db64SCarsten Haitzler
append_scripts_in_dir(const char * path)157*7391db64SCarsten Haitzler static void append_scripts_in_dir(const char *path)
158*7391db64SCarsten Haitzler {
159*7391db64SCarsten Haitzler struct dirent **entlist;
160*7391db64SCarsten Haitzler struct dirent *ent;
161*7391db64SCarsten Haitzler int n_dirs, i;
162*7391db64SCarsten Haitzler char filename[PATH_MAX];
163*7391db64SCarsten Haitzler
164*7391db64SCarsten Haitzler /* List files, sorted by alpha */
165*7391db64SCarsten Haitzler n_dirs = scandir(path, &entlist, NULL, alphasort);
166*7391db64SCarsten Haitzler if (n_dirs == -1)
167*7391db64SCarsten Haitzler return;
168*7391db64SCarsten Haitzler for (i = 0; i < n_dirs && (ent = entlist[i]); i++) {
169*7391db64SCarsten Haitzler if (ent->d_name[0] == '.')
170*7391db64SCarsten Haitzler continue; /* Skip hidden files */
171*7391db64SCarsten Haitzler if (is_test_script(path, ent->d_name)) { /* It's a test */
172*7391db64SCarsten Haitzler char bf[256];
173*7391db64SCarsten Haitzler const char *desc = shell_test__description
174*7391db64SCarsten Haitzler (bf, sizeof(bf), path, ent->d_name);
175*7391db64SCarsten Haitzler
176*7391db64SCarsten Haitzler if (desc) /* It has a desc line - valid script */
177*7391db64SCarsten Haitzler append_script(path, ent->d_name, desc);
178*7391db64SCarsten Haitzler } else if (is_directory(path, ent)) { /* Scan the subdir */
179*7391db64SCarsten Haitzler path__join(filename, sizeof(filename),
180*7391db64SCarsten Haitzler path, ent->d_name);
181*7391db64SCarsten Haitzler append_scripts_in_dir(filename);
182*7391db64SCarsten Haitzler }
183*7391db64SCarsten Haitzler }
184*7391db64SCarsten Haitzler for (i = 0; i < n_dirs; i++) /* Clean up */
185*7391db64SCarsten Haitzler zfree(&entlist[i]);
186*7391db64SCarsten Haitzler free(entlist);
187*7391db64SCarsten Haitzler }
188*7391db64SCarsten Haitzler
list_script_files(void)189*7391db64SCarsten Haitzler const struct script_file *list_script_files(void)
190*7391db64SCarsten Haitzler {
191*7391db64SCarsten Haitzler char path_dir[PATH_MAX];
192*7391db64SCarsten Haitzler const char *path;
193*7391db64SCarsten Haitzler
194*7391db64SCarsten Haitzler if (files)
195*7391db64SCarsten Haitzler return files; /* Singleton - we already know our list */
196*7391db64SCarsten Haitzler
197*7391db64SCarsten Haitzler path = shell_tests__dir(path_dir, sizeof(path_dir)); /* Walk dir */
198*7391db64SCarsten Haitzler append_scripts_in_dir(path);
199*7391db64SCarsten Haitzler
200*7391db64SCarsten Haitzler return files;
201*7391db64SCarsten Haitzler }
202*7391db64SCarsten Haitzler
list_script_max_width(void)203*7391db64SCarsten Haitzler int list_script_max_width(void)
204*7391db64SCarsten Haitzler {
205*7391db64SCarsten Haitzler list_script_files(); /* Ensure we have scanned all scripts */
206*7391db64SCarsten Haitzler return files_max_width;
207*7391db64SCarsten Haitzler }
208