diff options
Diffstat (limited to '')
-rw-r--r-- | nsswitch/stress-nss-libwbclient.c | 323 |
1 files changed, 323 insertions, 0 deletions
diff --git a/nsswitch/stress-nss-libwbclient.c b/nsswitch/stress-nss-libwbclient.c new file mode 100644 index 0000000..d9dc3b5 --- /dev/null +++ b/nsswitch/stress-nss-libwbclient.c @@ -0,0 +1,323 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <pthread.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <stdlib.h> +#include <sys/types.h> +#include <pwd.h> +#include <wbclient.h> +#include <sys/socket.h> +#include <errno.h> +#include <assert.h> + +#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; +} |