/* 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" #include "tests/common.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", NULL }, { "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", NULL }, 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); tests_set_cwd(); 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); }