summaryrefslogtreecommitdiffstats
path: root/src/tests/stress-tests.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/tests/stress-tests.c')
-rw-r--r--src/tests/stress-tests.c328
1 files changed, 328 insertions, 0 deletions
diff --git a/src/tests/stress-tests.c b/src/tests/stress-tests.c
new file mode 100644
index 00000000..94505318
--- /dev/null
+++ b/src/tests/stress-tests.c
@@ -0,0 +1,328 @@
+/*
+ SSSD
+
+ Stress tests
+
+ Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 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 <http://www.gnu.org/licenses/>.
+*/
+
+#include <signal.h>
+#include <stdlib.h>
+#include <talloc.h>
+#include <popt.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <pwd.h>
+#include <grp.h>
+#include <errno.h>
+
+#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 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 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 enoent_fail)
+{
+ if (group) {
+ return test_lookup_group(name, enoent_fail);
+ } else {
+ return test_lookup_user(name, 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 idx = 0;
+
+ out = talloc_array(mem_ctx, char *, num_names+1);
+ if (out == NULL) {
+ return ENOMEM;
+ }
+
+ for (idx = 0; idx < num_names; ++idx) {
+ out[idx] = talloc_asprintf(mem_ctx, "%s%d", prefix, idx);
+ if (out[idx] == NULL) {
+ return ENOMEM;
+ }
+ }
+ out[idx] = 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, idx, 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, 0,
+ "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 */
+ idx = 0;
+ for (idx=0; names[idx]; idx++) {
+ 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[idx], pc_groups, 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",
+ idx,
+ idx - failure_count,
+ failure_count);
+ }
+ return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE);
+}