stress-tests.c revision 1bbaf1a7cfde76bd81cab964c2eb9c91c6d8feba
1N/A/*
1N/A SSSD
1N/A
1N/A Stress tests
1N/A
1N/A Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
1N/A
1N/A This program is free software; you can redistribute it and/or modify
1N/A it under the terms of the GNU General Public License as published by
1N/A the Free Software Foundation; either version 3 of the License, or
1N/A (at your option) any later version.
1N/A
1N/A This program is distributed in the hope that it will be useful,
1N/A but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A GNU General Public License for more details.
1N/A
1N/A You should have received a copy of the GNU General Public License
1N/A along with this program. If not, see <http://www.gnu.org/licenses/>.
1N/A*/
1N/A
1N/A#include <signal.h>
1N/A#include <stdlib.h>
1N/A#include <talloc.h>
1N/A#include <popt.h>
1N/A#include <sys/types.h>
1N/A#include <sys/wait.h>
1N/A#include <pwd.h>
1N/A#include <grp.h>
1N/A#include <errno.h>
1N/A
1N/A#include "util/util.h"
1N/A
1N/A#define DEFAULT_START 10
1N/A#define DEFAULT_STOP 20
1N/A
1N/A#define NAME_SIZE 255
1N/A#define CHUNK 64
1N/A
1N/A
1N/A/* How many tests failed */
1N/Aint failure_count;
1N/A
1N/A/* Be chatty */
1N/Aint verbose;
1N/A
1N/A/*
1N/A * Look up one user. If the user is not found using getpwnam, the success
1N/A * or failure depends on enoent_fail being set.
1N/A */
1N/Aint test_lookup_user(const char *name, int enoent_fail)
1N/A{
1N/A struct passwd *pwd = NULL;
1N/A int ret = 0;
1N/A int error;
1N/A
1N/A errno = 0;
1N/A pwd = getpwnam(name);
1N/A error = errno;
1N/A if (pwd == NULL) {
1N/A if (errno == 0 || errno == ENOENT) {
1N/A ret = (enoent_fail == 1) ? ENOENT : 0;
1N/A }
1N/A }
1N/A
1N/A if (ret != 0 && verbose) {
1N/A fprintf(stderr,
1N/A "getpwnam failed (name: %s): errno = %d, error = %s\n",
1N/A name, ret, strerror(ret));
1N/A }
1N/A
1N/A return ret;
1N/A}
1N/A
1N/A/*
1N/A * Look up one group. If the user is not found using getgrnam, the success
1N/A * or failure depends on enoent_fail being set.
1N/A */
1N/Aint test_lookup_group(const char *name, int enoent_fail)
1N/A{
1N/A struct group *grp = NULL;
1N/A int ret = 0;
1N/A
1N/A errno = 0;
1N/A grp = getgrnam(name);
1N/A if (grp == NULL) {
1N/A if (errno == 0 || errno == ENOENT) {
1N/A ret = enoent_fail ? ENOENT : 0;
1N/A }
1N/A }
1N/A
1N/A if (ret != 0 && verbose) {
1N/A fprintf(stderr,
1N/A "getgrnam failed (name %s): errno = %d, error = %s\n",
1N/A name, ret, strerror(ret));
1N/A }
1N/A
1N/A return ret;
1N/A}
1N/A
1N/Aint run_one_testcase(const char *name, int group, int enoent_fail)
1N/A{
1N/A if (group) {
1N/A return test_lookup_group(name, enoent_fail);
1N/A } else {
1N/A return test_lookup_user(name, enoent_fail);
1N/A }
1N/A}
1N/A
1N/A/*
1N/A * Beware, has side-effects: changes global variable failure_count
1N/A */
1N/Avoid child_handler(int signum)
1N/A{
1N/A int status, ret;
1N/A
1N/A while ((ret = wait(&status)) > 0) {
1N/A if (ret == -1) {
1N/A perror("wait");
1N/A exit(EXIT_FAILURE);
1N/A }
1N/A
1N/A if (WIFEXITED(status)) {
1N/A ret = WEXITSTATUS(status);
1N/A if (ret) {
1N/A if (verbose) {
1N/A fprintf(stderr,
1N/A "A child exited with error code %d\n",
1N/A WEXITSTATUS(status));
1N/A }
1N/A ++failure_count;
1N/A }
1N/A } else ++failure_count;
1N/A }
1N/A}
1N/A
1N/Aint generate_names(TALLOC_CTX *mem_ctx, const char *prefix,
1N/A int start, int stop, char ***_out)
1N/A{
1N/A char **out;
1N/A int num_names = stop-start+1;
1N/A int idx = 0;
1N/A
1N/A out = talloc_array(mem_ctx, char *, num_names+1);
1N/A if (out == NULL) {
1N/A return ENOMEM;
1N/A }
1N/A
1N/A for (idx = 0; idx < num_names; ++idx) {
1N/A out[idx] = talloc_asprintf(mem_ctx, "%s%d", prefix, idx);
1N/A if (out[idx] == NULL) {
1N/A return ENOMEM;
1N/A }
1N/A }
1N/A out[idx] = NULL;
1N/A
1N/A *_out = out;
1N/A return EOK;
1N/A}
1N/A
1N/Aint read_names(TALLOC_CTX *mem_ctx, FILE *stream, char ***_out)
1N/A{
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);
}