1 /*
2 * Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
3 * Copyright (c) International Business Machines Corp., 2001
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of
8 * the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it would be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 * Description:
19 * This program is designed to stress the NFS implimentation. Many bugs were
20 * uncovered in the AIX operating system implimentation of NFS when AIX kernel
21 * was built over NFS. Source directory on a remote machine (one server many
22 * clients) NFS-mounted on to a directory on a local machine from which the
23 * kernel build was initiated. Apparently many defects/bugs were uncovered when
24 * multiple users tried to build the kernel by NFS mounting the kernel source
25 * from a remote machine and tried to build the kernel on a local machine.
26 *
27 * The test's aimed to stress NFS client/server and recreates such a senario.
28 * Spawn N number of threads. Each thread does the following:
29 * * create a directory tree;
30 * * populate it with ".c" files and makefiles;
31 * hostname.1234
32 * | - 1234.0.0.c
33 * | - ..........
34 * | - makefile
35 * |_ 1234.0
36 * |
37 * | - 1234.1.0.c
38 * | - ..........
39 * | - makefile
40 * |_ 1234.1
41 * |....
42 *
43 * * initate a build, executable will print hello world;
44 * * clean up all the executables that were created;
45 * * recurssively remove each subdir and its contents.
46 *
47 */
48
49 #define _GNU_SOURCE
50 #include <stdio.h>
51 #include <sys/stat.h>
52 #include <sys/wait.h>
53 #include <unistd.h>
54 #include <stdlib.h>
55 #include <fcntl.h>
56 #include <unistd.h>
57 #include <pthread.h>
58 #include <sys/mount.h>
59 #include <linux/limits.h>
60 #include <errno.h>
61 #include <linux/unistd.h>
62
63 #include "lapi/mkdirat.h"
64 #include "tst_safe_pthread.h"
65 #include "tst_safe_stdio.h"
66 #include "tst_test.h"
67
68 #define gettid() syscall(__NR_gettid)
69
70 static int thrd_num = 8;
71 static int dirs_num = 100;
72 static int file_num = 100;
73
74 static char *t_arg, *d_arg, *f_arg;
75
76 static struct tst_option opts[] = {
77 {"t:", &t_arg, "-t x Number of threads to generate, default: 8\n"},
78 {"d:", &d_arg, "-d x Number of subdirs to generate, default: 100\n"},
79 {"f:", &f_arg, "-f x Number of c files in each dir, default: 100\n"},
80 {NULL, NULL, NULL}
81 };
82
run_targets(const char * dirname,char * cfile,pid_t tid)83 static void run_targets(const char *dirname, char *cfile, pid_t tid)
84 {
85 int i, k, fd;
86 char subdir[PATH_MAX] = {0};
87 char *output_file;
88 char buf[11];
89 const char *const cmd_run[] = {cfile, NULL};
90
91 SAFE_ASPRINTF(&output_file, "%s/cmd.out", dirname);
92
93 /* run each binary */
94 for (i = 0; i < dirs_num; ++i) {
95 for (k = 0; k < file_num; ++k) {
96 snprintf(cfile, PATH_MAX, "%s%s/%d.%d.%d",
97 dirname, subdir, tid, i, k);
98
99 tst_run_cmd(cmd_run, output_file, NULL, 0);
100
101 fd = SAFE_OPEN(output_file, O_RDONLY);
102 SAFE_READ(1, fd, buf, 11);
103 if (strncmp(buf, "hello world", 11))
104 tst_brk(TFAIL, "command printed wrong message");
105 SAFE_CLOSE(fd);
106 }
107 strcat(subdir, "/dir");
108 }
109
110 free(output_file);
111 }
112
thread_fn(LTP_ATTRIBUTE_UNUSED void * args)113 static void *thread_fn(LTP_ATTRIBUTE_UNUSED void *args)
114 {
115 const char prog_buf[] = "#include <stdio.h>\n"
116 "int main(void)\n{\n"
117 "\tprintf(\"hello world\");\n"
118 "\treturn 0;\n}\n";
119
120 const char make_buf_n[] = "CFLAGS := -O -w -g\n"
121 "SRCS=$(wildcard *.c)\n"
122 "TARGETS=$(SRCS:.c=)\n"
123 "all: $(TARGETS)\n"
124 "$(TARGETS): %: %.c\n"
125 "\t$(CC) -o $@ $<\n"
126 "clean:\n\trm -f $(TARGETS)\n"
127 ".PHONY: all clean\n";
128
129 const char make_buf[] = "CFLAGS := -O -w -g\n"
130 "SUBDIR = dir\n"
131 "SRCS=$(wildcard *.c)\n"
132 "TARGETS=$(SRCS:.c=)\n"
133 "all: $(SUBDIR) $(TARGETS)\n"
134 "$(TARGETS): %: %.c\n"
135 "\t$(CC) -o $@ $<\n"
136 "$(SUBDIR):\n\t$(MAKE) -C $@\n"
137 "clean:\n"
138 "\trm -f $(TARGETS)\n"
139 "\t$(MAKE) -C $(SUBDIR) clean\n"
140 ".PHONY: all $(SUBDIR) clean\n";
141
142 int i, k, fd, dirfd, ret;
143 char *dirname;
144 char cfile[PATH_MAX];
145 char hostname[256];
146 pid_t tid = gettid();
147
148 SAFE_GETHOSTNAME(hostname, 256);
149 SAFE_ASPRINTF(&dirname, "%s.%ld", hostname, tid);
150
151 SAFE_MKDIR(dirname, 0755);
152 dirfd = SAFE_OPEN(dirname, O_DIRECTORY);
153
154 for (i = 0; i < dirs_num; ++i) {
155
156 fd = openat(dirfd, "makefile", O_CREAT | O_RDWR,
157 S_IRWXU | S_IRWXG | S_IRWXO);
158 if (fd < 0)
159 tst_brk(TFAIL | TERRNO, "openat(makefile) failed");
160
161 if (i == dirs_num - 1)
162 SAFE_WRITE(1, fd, make_buf_n, sizeof(make_buf_n) - 1);
163 else
164 SAFE_WRITE(1, fd, make_buf, sizeof(make_buf) - 1);
165
166 SAFE_CLOSE(fd);
167
168 for (k = 0; k < file_num; ++k) {
169 snprintf(cfile, PATH_MAX, "%d.%d.%d.c", tid, i, k);
170 fd = openat(dirfd, cfile, O_CREAT | O_RDWR,
171 S_IRWXU | S_IRWXG | S_IRWXO);
172 if (fd < 0) {
173 tst_brk(TFAIL | TERRNO,
174 "openat(%s) failed", cfile);
175 }
176
177 SAFE_WRITE(1, fd, prog_buf, sizeof(prog_buf) - 1);
178 SAFE_CLOSE(fd);
179 }
180
181 if (i == dirs_num - 1)
182 break;
183
184 ret = mkdirat(dirfd, "dir", 0755);
185 if (ret < 0)
186 tst_brk(TFAIL | TERRNO, "mkdirat('dir') failed");
187 dirfd = openat(dirfd, "dir", O_DIRECTORY);
188 if (dirfd < 0)
189 tst_brk(TFAIL | TERRNO, "openat('dir') failed");
190 }
191
192 const char *const cmd_make[] = {"make", "-s", "-C", dirname, NULL};
193 const char *const cmd_make_clean[] = {
194 "make", "-C", dirname, "-s", "clean", NULL};
195
196 tst_run_cmd(cmd_make, NULL, NULL, 0);
197
198 run_targets(dirname, cfile, tid);
199
200 tst_run_cmd(cmd_make_clean, NULL, NULL, 0);
201
202 free(dirname);
203
204 return NULL;
205 }
206
setup(void)207 static void setup(void)
208 {
209 thrd_num = atoi(t_arg);
210 dirs_num = atoi(d_arg);
211 file_num = atoi(f_arg);
212 }
213
do_test(void)214 static void do_test(void)
215 {
216 int i;
217 pthread_t id[thrd_num];
218
219 for (i = 0; i < thrd_num; ++i)
220 SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, NULL);
221
222 for (i = 0; i < thrd_num; ++i)
223 SAFE_PTHREAD_JOIN(id[i], NULL);
224
225 tst_res(TPASS, "'make' successfully build and clean all targets");
226 }
227
228 static struct tst_test test = {
229 .options = opts,
230 .test_all = do_test,
231 .setup = setup,
232 };
233