/*
* Copyright (c) 2016 Oracle and/or its affiliates. All Rights Reserved.
* Copyright (c) International Business Machines Corp., 2001
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of
* the License, or (at your option) any later version.
*
* This program is distributed in the hope that it would be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
* Description:
* This program is designed to stress the NFS implimentation. Many bugs were
* uncovered in the AIX operating system implimentation of NFS when AIX kernel
* was built over NFS. Source directory on a remote machine (one server many
* clients) NFS-mounted on to a directory on a local machine from which the
* kernel build was initiated. Apparently many defects/bugs were uncovered when
* multiple users tried to build the kernel by NFS mounting the kernel source
* from a remote machine and tried to build the kernel on a local machine.
*
* The test's aimed to stress NFS client/server and recreates such a senario.
* Spawn N number of threads. Each thread does the following:
* * create a directory tree;
* * populate it with ".c" files and makefiles;
* hostname.1234
* | - 1234.0.0.c
* | - ..........
* | - makefile
* |_ 1234.0
* |
* | - 1234.1.0.c
* | - ..........
* | - makefile
* |_ 1234.1
* |....
*
* * initate a build, executable will print hello world;
* * clean up all the executables that were created;
* * recurssively remove each subdir and its contents.
*
*/
#define _GNU_SOURCE
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "lapi/mkdirat.h"
#include "tst_safe_pthread.h"
#include "tst_safe_stdio.h"
#include "tst_test.h"
#define gettid() syscall(__NR_gettid)
static int thrd_num = 8;
static int dirs_num = 100;
static int file_num = 100;
static char *t_arg, *d_arg, *f_arg;
static struct tst_option opts[] = {
{"t:", &t_arg, "-t x Number of threads to generate, default: 8\n"},
{"d:", &d_arg, "-d x Number of subdirs to generate, default: 100\n"},
{"f:", &f_arg, "-f x Number of c files in each dir, default: 100\n"},
{NULL, NULL, NULL}
};
static void run_targets(const char *dirname, char *cfile, pid_t tid)
{
int i, k, fd;
char subdir[PATH_MAX] = {0};
char *output_file;
char buf[11];
const char *const cmd_run[] = {cfile, NULL};
SAFE_ASPRINTF(&output_file, "%s/cmd.out", dirname);
/* run each binary */
for (i = 0; i < dirs_num; ++i) {
for (k = 0; k < file_num; ++k) {
snprintf(cfile, PATH_MAX, "%s%s/%d.%d.%d",
dirname, subdir, tid, i, k);
tst_run_cmd(cmd_run, output_file, NULL, 0);
fd = SAFE_OPEN(output_file, O_RDONLY);
SAFE_READ(1, fd, buf, 11);
if (strncmp(buf, "hello world", 11))
tst_brk(TFAIL, "command printed wrong message");
SAFE_CLOSE(fd);
}
strcat(subdir, "/dir");
}
free(output_file);
}
static void *thread_fn(LTP_ATTRIBUTE_UNUSED void *args)
{
const char prog_buf[] = "#include \n"
"int main(void)\n{\n"
"\tprintf(\"hello world\");\n"
"\treturn 0;\n}\n";
const char make_buf_n[] = "CFLAGS := -O -w -g\n"
"SRCS=$(wildcard *.c)\n"
"TARGETS=$(SRCS:.c=)\n"
"all: $(TARGETS)\n"
"$(TARGETS): %: %.c\n"
"\t$(CC) -o $@ $<\n"
"clean:\n\trm -f $(TARGETS)\n"
".PHONY: all clean\n";
const char make_buf[] = "CFLAGS := -O -w -g\n"
"SUBDIR = dir\n"
"SRCS=$(wildcard *.c)\n"
"TARGETS=$(SRCS:.c=)\n"
"all: $(SUBDIR) $(TARGETS)\n"
"$(TARGETS): %: %.c\n"
"\t$(CC) -o $@ $<\n"
"$(SUBDIR):\n\t$(MAKE) -C $@\n"
"clean:\n"
"\trm -f $(TARGETS)\n"
"\t$(MAKE) -C $(SUBDIR) clean\n"
".PHONY: all $(SUBDIR) clean\n";
int i, k, fd, dirfd, ret;
char *dirname;
char cfile[PATH_MAX];
char hostname[256];
pid_t tid = gettid();
SAFE_GETHOSTNAME(hostname, 256);
SAFE_ASPRINTF(&dirname, "%s.%ld", hostname, tid);
SAFE_MKDIR(dirname, 0755);
dirfd = SAFE_OPEN(dirname, O_DIRECTORY);
for (i = 0; i < dirs_num; ++i) {
fd = openat(dirfd, "makefile", O_CREAT | O_RDWR,
S_IRWXU | S_IRWXG | S_IRWXO);
if (fd < 0)
tst_brk(TFAIL | TERRNO, "openat(makefile) failed");
if (i == dirs_num - 1)
SAFE_WRITE(1, fd, make_buf_n, sizeof(make_buf_n) - 1);
else
SAFE_WRITE(1, fd, make_buf, sizeof(make_buf) - 1);
SAFE_CLOSE(fd);
for (k = 0; k < file_num; ++k) {
snprintf(cfile, PATH_MAX, "%d.%d.%d.c", tid, i, k);
fd = openat(dirfd, cfile, O_CREAT | O_RDWR,
S_IRWXU | S_IRWXG | S_IRWXO);
if (fd < 0) {
tst_brk(TFAIL | TERRNO,
"openat(%s) failed", cfile);
}
SAFE_WRITE(1, fd, prog_buf, sizeof(prog_buf) - 1);
SAFE_CLOSE(fd);
}
if (i == dirs_num - 1)
break;
ret = mkdirat(dirfd, "dir", 0755);
if (ret < 0)
tst_brk(TFAIL | TERRNO, "mkdirat('dir') failed");
dirfd = openat(dirfd, "dir", O_DIRECTORY);
if (dirfd < 0)
tst_brk(TFAIL | TERRNO, "openat('dir') failed");
}
const char *const cmd_make[] = {"make", "-s", "-C", dirname, NULL};
const char *const cmd_make_clean[] = {
"make", "-C", dirname, "-s", "clean", NULL};
tst_run_cmd(cmd_make, NULL, NULL, 0);
run_targets(dirname, cfile, tid);
tst_run_cmd(cmd_make_clean, NULL, NULL, 0);
free(dirname);
return NULL;
}
static void setup(void)
{
thrd_num = atoi(t_arg);
dirs_num = atoi(d_arg);
file_num = atoi(f_arg);
}
static void do_test(void)
{
int i;
pthread_t id[thrd_num];
for (i = 0; i < thrd_num; ++i)
SAFE_PTHREAD_CREATE(id + i, NULL, thread_fn, NULL);
for (i = 0; i < thrd_num; ++i)
SAFE_PTHREAD_JOIN(id[i], NULL);
tst_res(TPASS, "'make' successfully build and clean all targets");
}
static struct tst_test test = {
.options = opts,
.test_all = do_test,
.setup = setup,
};