From 4e0c3d5ee856dfc2568d4348fc48a9eb975c66f6 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Tue, 14 Apr 2009 13:53:45 +0200 Subject: Stress test --- server/Makefile.in | 2 +- server/server.mk | 6 + server/tests/stress-tests.c | 326 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 server/tests/stress-tests.c diff --git a/server/Makefile.in b/server/Makefile.in index 8aabd2f07..69f74a368 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -133,7 +133,7 @@ TOOLSBINS = sbin/sss_useradd sbin/sss_userdel sbin/sss_groupadd sbin/sss_groupde BINS = sbin/sssd $(LIBEXECBINS) $(TOOLSBINS) ifneq (x$(HAVE_TESTS), x) - TESTS = tests/sysdb-tests tests/infopipe-tests + TESTS = tests/sysdb-tests tests/infopipe-tests tests/stress-tests TESTS_DATA = tests/tests.ldb tests/tests_conf.ldb tests/introspect.ref endif diff --git a/server/server.mk b/server/server.mk index 07848939e..2085d95a3 100644 --- a/server/server.mk +++ b/server/server.mk @@ -63,6 +63,9 @@ SYSDB_TEST_OBJ = \ INFP_TEST_OBJ = \ tests/infopipe-tests.o +STRESS_TEST_OBJ = \ + tests/stress-tests.o + CRYPT_OBJ = \ util/nss_sha512crypt.o @@ -138,6 +141,9 @@ tests/sysdb-tests: $(SYSDB_TEST_OBJ) $(UTIL_OBJ) tests/infopipe-tests: $(INFP_TEST_OBJ) $(UTIL_OBJ) $(CC) -o tests/infopipe-tests $(INFP_TEST_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) $(CHECK_LIBS) +tests/stress-tests: $(STRESS_TEST_OBJ) $(UTIL_OBJ) + $(CC) -o tests/stress-tests $(STRESS_TEST_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) $(CHECK_LIBS) + #Tools sbin/sss_useradd: $(USERADD_OBJ) $(TOOLS_OBJ) $(UTIL_OBJ) $(CC) -o sbin/sss_useradd $(USERADD_OBJ) $(TOOLS_OBJ) $(UTIL_OBJ) $(LDFLAGS) $(LIBS) diff --git a/server/tests/stress-tests.c b/server/tests/stress-tests.c new file mode 100644 index 000000000..8fdcf9894 --- /dev/null +++ b/server/tests/stress-tests.c @@ -0,0 +1,326 @@ +/* + SSSD + + Stress tests + + Copyright (C) Jakub Hrozek 2009 + + 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 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will 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 . +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "util/util.h" + +#define DEFAULT_START 10 +#define DEFAULT_STOP 20 + +#define NAME_SIZE 255 +#define CHUNK 64 + + +/* How many tests failed */ +int failure_count; + +/* Be chatty */ +int verbose; + +/* + * Look up one user. If the user is not found using getpwnam, the success + * or failure depends on enoent_fail being set. + */ +int test_lookup_user(const char *name, int verbose, int enoent_fail) +{ + struct passwd *pwd = NULL; + int ret = 0; + int error; + + errno = 0; + pwd = getpwnam(name); + error = errno; + if (pwd == NULL) { + if (errno == 0 || errno == ENOENT) { + ret = (enoent_fail == 1) ? ENOENT : 0; + } + } + + if (ret != 0 && verbose) { + fprintf(stderr, + "getpwnam failed (name: %s): errno = %d, error = %s\n", + name, ret, strerror(ret)); + } + + return ret; +} + +/* + * Look up one group. If the user is not found using getgrnam, the success + * or failure depends on enoent_fail being set. + */ +int test_lookup_group(const char *name, int verbose, int enoent_fail) +{ + struct group *grp = NULL; + int ret = 0; + + errno = 0; + grp = getgrnam(name); + if (grp == NULL) { + if (errno == 0 || errno == ENOENT) { + ret = enoent_fail ? ENOENT : 0; + } + } + + if (ret != 0 && verbose) { + fprintf(stderr, + "getgrnam failed (name %s): errno = %d, error = %s\n", + name, ret, strerror(ret)); + } + + return ret; +} + +int run_one_testcase(const char *name, int group, int verbose, int enoent_fail) +{ + return group ? test_lookup_group(name, verbose, enoent_fail) : \ + test_lookup_user(name, verbose, enoent_fail); +} + +/* + * Beware, has side-effects: changes global variable failure_count + */ +void child_handler(int signum) +{ + int status, ret; + + while ((ret = wait(&status)) > 0) { + if (ret == -1) { + perror("wait"); + exit(EXIT_FAILURE); + } + + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + if (ret) { + if (verbose) { + fprintf(stderr, + "A child exited with error code %d\n", + WEXITSTATUS(status)); + } + ++failure_count; + } + } else ++failure_count; + } +} + +int generate_names(TALLOC_CTX *mem_ctx, const char *prefix, + int start, int stop, char ***_out) +{ + char **out; + int num_names = stop-start+1; + int index = 0; + + out = talloc_array(mem_ctx, char *, num_names+1); + if (out == NULL) { + return ENOMEM; + } + + for (index = 0; index < num_names; ++index) { + out[index] = talloc_asprintf(mem_ctx, "%s%d", prefix, index); + if (out[index] == NULL) { + return ENOMEM; + } + } + out[index] = NULL; + + *_out = out; + return EOK; +} + +int read_names(TALLOC_CTX *mem_ctx, FILE *stream, char ***_out) +{ + char one_name[NAME_SIZE]; + int n = 0; + int array_size = CHUNK; + int ret; + char **out; + + out = talloc_array(mem_ctx, char *, CHUNK+1); + if (out == NULL) { + return ENOMEM; + } + while (fgets(one_name, NAME_SIZE, stream)) { + out[n] = talloc_strdup(mem_ctx, one_name); + if (out[n] == NULL) { + return ENOMEM; + } + if ((n++ % CHUNK) == 0) { + array_size += CHUNK; + out = talloc_realloc(mem_ctx, out, char *, array_size); + if (out == NULL) { + return ENOMEM; + } + } + } + + if (ret = ferror(stream)) { + return ret; + } + out[n] = NULL; + + *_out = out; + return EOK; +} + +int main(int argc, const char *argv[]) +{ + int opt; + poptContext pc; + int pc_start=DEFAULT_START; + int pc_stop=DEFAULT_STOP; + int pc_enoent_fail=0; + int pc_groups=0; + int pc_verbosity = 0; + char *pc_prefix = NULL; + TALLOC_CTX *ctx = NULL; + char **names = NULL; + + int status, index, ret; + pid_t pid; + struct sigaction action, old_action; + + struct poptOption long_options[] = { + POPT_AUTOHELP + { "groups", 'g', POPT_ARG_NONE, &pc_groups, 0, + "Lookup in groups instead of users" }, + { "prefix", '\0', POPT_ARG_STRING, &pc_prefix, NULL, + "The username prefix", NULL }, + { "start", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &pc_start, 0, + "Start value to append to prefix", NULL }, + { "stop", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, + &pc_stop, 0, + "End value to append to prefix", NULL }, + { "enoent-fail", '\0', POPT_ARG_NONE, &pc_enoent_fail, 0, + "Fail on not getting the requested NSS data (default: No)", + NULL }, + { "verbose", 'v', POPT_ARG_NONE, 0, 'v', + "Be verbose" }, + POPT_TABLEEND + }; + + /* parse the params */ + pc = poptGetContext(argv[0], argc, argv, long_options, 0); + while ((opt = poptGetNextOpt(pc)) != -1) { + switch (opt) { + case 'v': + pc_verbosity = 1; + break; + + default: + fprintf(stderr, "\nInvalid option %s: %s\n\n", + poptBadOption(pc, 0), poptStrerror(opt)); + poptPrintUsage(pc, stderr, 0); + return 1; + } + } + poptFreeContext(pc); + + verbose = pc_verbosity; + + if (pc_prefix) { + ret = generate_names(ctx, pc_prefix, pc_start, pc_stop, &names); + if (ret != EOK) { + if (verbose) { + errno = ret; + perror("generate_names"); + } + exit(EXIT_FAILURE); + } + } else { + ret = read_names(ctx, stdin, &names); + if (ret != EOK) { + if (verbose) { + errno = ret; + perror("read_names"); + } + exit(EXIT_FAILURE); + } + } + + /* Reap the children in a handler asynchronously so we can + * somehow protect against too many processes */ + action.sa_handler = child_handler; + sigemptyset(&action.sa_mask); + sigaddset(&action.sa_mask, SIGCHLD); + action.sa_flags = SA_NOCLDSTOP; + + sigaction(SIGCHLD, &action, &old_action); + + /* Fire up the child processes */ + index = 0; + for (index=0; names[index]; index++) { + pid = fork(); + if (pid == -1) { + /* Try again in hope that some child has exited */ + if (errno == EAGAIN) { + continue; + } + perror("fork"); + exit(EXIT_FAILURE); + } else if ( pid == 0 ) { + /* child */ + ret = run_one_testcase(names[index], pc_groups, + pc_verbosity, pc_enoent_fail); + exit(ret); + } + } + + /* Process the rest of the children here in main */ + sigaction(SIGCHLD, &old_action, NULL); + while ((ret = wait(&status)) > 0) { + if (ret == -1) { + perror("wait"); + exit(EXIT_FAILURE); + } + + if (WIFEXITED(status)) { + ret = WEXITSTATUS(status); + if (ret) { + if (verbose) { + fprintf(stderr, + "A child exited with error code %d\n", + WEXITSTATUS(status)); + } + ++failure_count; + } + } else ++failure_count; + } + + if (pc_verbosity) { + fprintf(stderr, + "Total tests run: %d\nPassed: %d\nFailed: %d\n", + index, + index - failure_count, + failure_count); + } + return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE); +} -- cgit