/*
Unix SMB/CIFS implementation.
Stress test for parallel NSS & libwbclient calls.
Copyright (C) Ralph Wuerthner 2018
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
#include
#include
#include
#include
#define RUNTIME 10
struct thread_state {
const char *username;
time_t timeout;
pthread_mutex_t lock;
bool fail;
int nss_loop_count;
int wbc_loop_count;
};
static void *query_nss_thread(void *ptr)
{
struct thread_state *state = ptr;
char buf[1024];
ssize_t nread, nwritten;
int p[2];
int rc;
struct passwd pwd, *result;
pid_t pid;
while (time(NULL) < state->timeout) {
rc = getpwnam_r(state->username,
&pwd,
buf,
sizeof(buf),
&result);
if (rc != 0 || result == NULL) {
pthread_mutex_lock(&state->lock);
state->fail = true;
pthread_mutex_unlock(&state->lock);
fprintf(stderr,
"getpwnam_r failed with rc='%s' result=%p\n",
strerror(rc),
result);
break;
}
state->nss_loop_count++;
pthread_mutex_lock(&state->lock);
if (state->fail) {
pthread_mutex_unlock(&state->lock);
break;
}
pthread_mutex_unlock(&state->lock);
}
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
if (rc != 0) {
state->fail = true;
return NULL;
}
/*
* Check getpwnam_r() still works after a fork,
* both in parent and child.
*/
pid = fork();
if (pid == -1) {
return NULL;
}
if (pid == 0) {
/* Child */
rc = getpwnam_r(state->username,
&pwd,
buf,
sizeof(buf),
&result);
if (rc != 0 || result == NULL) {
fprintf(stderr,
"getpwnam_r failed with rc='%s' result=%p\n",
strerror(rc),
result);
rc = 1;
nwritten = write(p[0], &rc, sizeof(int));
assert(nwritten == sizeof(int));
exit(1);
}
printf("child: getpwnam_r in child succeeded\n");
rc = 0;
nwritten = write(p[0], &rc, sizeof(int));
assert(nwritten == sizeof(int));
exit(1);
}
/* Parent */
/* Check result from child */
nread = read(p[1], &rc, sizeof(int));
if (nread != sizeof(int)) {
fprintf(stderr,
"read from child failed with errno='%s' nread=%zd\n",
strerror(errno),
nread);
state->fail = true;
return NULL;
}
if (rc != 0) {
fprintf(stderr,
"getpwnam_r failed in the child\n");
state->fail = true;
return NULL;
}
printf("parent: getpwnam_r in child succeeded\n");
/* Verify getpwnam_r() in parent after fork */
rc = getpwnam_r(state->username,
&pwd,
buf,
sizeof(buf),
&result);
if (rc != 0 || result == NULL) {
fprintf(stderr,
"getpwnam_r failed with rc='%s' result=%p\n",
strerror(rc),
result);
state->fail = true;
return NULL;
}
printf("parent: getpwnam_r in parent succeeded\n");
return NULL;
}
static void *query_wbc_thread(void *ptr)
{
struct thread_state *state = ptr;
struct passwd *ppwd;
wbcErr wbc_status;
pid_t pid;
ssize_t nread, nwritten;
int p[2];
int rc;
while (time(NULL) < state->timeout) {
wbc_status = wbcGetpwnam(state->username, &ppwd);
if (!WBC_ERROR_IS_OK(wbc_status)) {
pthread_mutex_lock(&state->lock);
state->fail = true;
pthread_mutex_unlock(&state->lock);
fprintf(stderr,
"wbcGetpwnam failed with %s\n",
wbcErrorString(wbc_status));
break;
}
wbcFreeMemory(ppwd);
state->wbc_loop_count++;
pthread_mutex_lock(&state->lock);
if (state->fail) {
pthread_mutex_unlock(&state->lock);
break;
}
pthread_mutex_unlock(&state->lock);
}
rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
if (rc != 0) {
state->fail = true;
return NULL;
}
/*
* Check wbcGetpwnam() still works after a fork,
* both in parent and child.
*/
pid = fork();
if (pid == -1) {
return NULL;
}
if (pid == 0) {
/* Child */
wbc_status = wbcGetpwnam(state->username, &ppwd);
if (!WBC_ERROR_IS_OK(wbc_status)) {
fprintf(stderr,
"wbcGetpwnam failed with %s\n",
wbcErrorString(wbc_status));
rc = 1;
nwritten = write(p[0], &rc, sizeof(int));
assert(nwritten == sizeof(int));
exit(1);
}
wbcFreeMemory(ppwd);
printf("child: wbcGetpwnam in child succeeded\n");
rc = 0;
nwritten = write(p[0], &rc, sizeof(int));
assert(nwritten == sizeof(int));
exit(1);
}
/* Parent */
/* Check result from child */
nread = read(p[1], &rc, sizeof(int));
if (nread != sizeof(int)) {
fprintf(stderr,
"read from child failed with errno='%s' nread=%zd\n",
strerror(errno),
nread);
state->fail = true;
return NULL;
}
if (rc != 0) {
fprintf(stderr,
"wbcGetpwnam failed in the child\n");
state->fail = true;
return NULL;
}
printf("parent: wbcGetpwnam in child succeeded\n");
/* Verify wbcGetpwnam() in parent after fork */
wbc_status = wbcGetpwnam(state->username, &ppwd);
if (!WBC_ERROR_IS_OK(wbc_status)) {
fprintf(stderr,
"wbcGetpwnam failed with %s\n",
wbcErrorString(wbc_status));
state->fail = true;
return NULL;
}
wbcFreeMemory(ppwd);
printf("parent: wbcGetpwnam in parent succeeded\n");
return NULL;
}
int main(int argc, char *argv[])
{
int rc, n;
struct thread_state state;
pthread_t threads[2];
if (argc < 2 ) {
fprintf(stderr,"%s: missing domain user\n", argv[0]);
return 1;
}
state.username = argv[1];
state.timeout = time(NULL) + RUNTIME;
rc = pthread_mutex_init(&state.lock, NULL);
if (rc != 0) {
fprintf(stderr,
"pthread_mutex_init failed: %s\n",
strerror(rc));
exit(1);
}
state.fail = false;
state.nss_loop_count = 0;
state.wbc_loop_count = 0;
printf("query domain user '%s'\n", state.username);
/* create query threads */
rc = pthread_create(&threads[0], NULL, query_nss_thread, &state);
if (rc != 0) {
fprintf(stderr,
"creating NSS thread failed: %s\n",
strerror(rc));
exit(1);
}
rc = pthread_create(&threads[1], NULL, query_wbc_thread, &state);
if (rc != 0) {
fprintf(stderr,
"creating libwbclient thread failed: %s\n",
strerror(rc));
exit(1);
}
/* wait for query threads to terminate */
for (n = 0; n < 2; n++) {
rc = pthread_join(threads[n], NULL);
if (rc != 0) {
fprintf(stderr,
"joining query thread %i failed: %s\n",
n,
strerror(rc));
exit(1);
}
}
fprintf(state.fail ? stderr: stdout,
"test %s with %i NSS and %i libwbclient calls\n",
state.fail ? "failed" : "passed",
state.nss_loop_count,
state.wbc_loop_count);
return state.fail;
}