Line data Source code
1 : /*
2 : Unix SMB/CIFS implementation.
3 :
4 : Stress test for parallel NSS & libwbclient calls.
5 :
6 : Copyright (C) Ralph Wuerthner 2018
7 :
8 : This program is free software; you can redistribute it and/or modify
9 : it under the terms of the GNU General Public License as published by
10 : the Free Software Foundation; either version 3 of the License, or
11 : (at your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful,
14 : but WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 : GNU General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program. If not, see <http://www.gnu.org/licenses/>.
20 : */
21 :
22 : #include <stdio.h>
23 : #include <stdint.h>
24 : #include <stdbool.h>
25 : #include <pthread.h>
26 : #include <string.h>
27 : #include <unistd.h>
28 : #include <time.h>
29 : #include <stdlib.h>
30 : #include <sys/types.h>
31 : #include <pwd.h>
32 : #include <wbclient.h>
33 : #include <sys/socket.h>
34 : #include <errno.h>
35 : #include <assert.h>
36 :
37 : #define RUNTIME 10
38 :
39 : struct thread_state {
40 : const char *username;
41 : time_t timeout;
42 : pthread_mutex_t lock;
43 : bool fail;
44 : int nss_loop_count;
45 : int wbc_loop_count;
46 : };
47 :
48 2 : static void *query_nss_thread(void *ptr)
49 : {
50 2 : struct thread_state *state = ptr;
51 : char buf[1024];
52 : ssize_t nread, nwritten;
53 : int p[2];
54 : int rc;
55 : struct passwd pwd, *result;
56 : pid_t pid;
57 :
58 47140 : while (time(NULL) < state->timeout) {
59 47138 : rc = getpwnam_r(state->username,
60 : &pwd,
61 : buf,
62 : sizeof(buf),
63 : &result);
64 47138 : if (rc != 0 || result == NULL) {
65 0 : pthread_mutex_lock(&state->lock);
66 0 : state->fail = true;
67 0 : pthread_mutex_unlock(&state->lock);
68 0 : fprintf(stderr,
69 : "getpwnam_r failed with rc='%s' result=%p\n",
70 : strerror(rc),
71 : result);
72 0 : break;
73 : }
74 47138 : state->nss_loop_count++;
75 47138 : pthread_mutex_lock(&state->lock);
76 47138 : if (state->fail) {
77 0 : pthread_mutex_unlock(&state->lock);
78 0 : break;
79 : }
80 47138 : pthread_mutex_unlock(&state->lock);
81 : }
82 :
83 2 : rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
84 2 : if (rc != 0) {
85 0 : state->fail = true;
86 0 : return NULL;
87 : }
88 :
89 : /*
90 : * Check getpwnam_r() still works after a fork,
91 : * both in parent and child.
92 : */
93 :
94 2 : pid = fork();
95 4 : if (pid == -1) {
96 0 : return NULL;
97 : }
98 4 : if (pid == 0) {
99 : /* Child */
100 2 : rc = getpwnam_r(state->username,
101 : &pwd,
102 : buf,
103 : sizeof(buf),
104 : &result);
105 2 : if (rc != 0 || result == NULL) {
106 0 : fprintf(stderr,
107 : "getpwnam_r failed with rc='%s' result=%p\n",
108 : strerror(rc),
109 : result);
110 0 : rc = 1;
111 0 : nwritten = write(p[0], &rc, sizeof(int));
112 0 : assert(nwritten == sizeof(int));
113 0 : exit(1);
114 : }
115 2 : printf("child: getpwnam_r in child succeeded\n");
116 2 : rc = 0;
117 2 : nwritten = write(p[0], &rc, sizeof(int));
118 2 : assert(nwritten == sizeof(int));
119 2 : exit(1);
120 : }
121 :
122 : /* Parent */
123 :
124 : /* Check result from child */
125 2 : nread = read(p[1], &rc, sizeof(int));
126 2 : if (nread != sizeof(int)) {
127 0 : fprintf(stderr,
128 : "read from child failed with errno='%s' nread=%zd\n",
129 0 : strerror(errno),
130 : nread);
131 0 : state->fail = true;
132 0 : return NULL;
133 : }
134 :
135 2 : if (rc != 0) {
136 0 : fprintf(stderr,
137 : "getpwnam_r failed in the child\n");
138 0 : state->fail = true;
139 0 : return NULL;
140 : }
141 2 : printf("parent: getpwnam_r in child succeeded\n");
142 :
143 : /* Verify getpwnam_r() in parent after fork */
144 2 : rc = getpwnam_r(state->username,
145 : &pwd,
146 : buf,
147 : sizeof(buf),
148 : &result);
149 2 : if (rc != 0 || result == NULL) {
150 0 : fprintf(stderr,
151 : "getpwnam_r failed with rc='%s' result=%p\n",
152 : strerror(rc),
153 : result);
154 0 : state->fail = true;
155 0 : return NULL;
156 : }
157 2 : printf("parent: getpwnam_r in parent succeeded\n");
158 2 : return NULL;
159 : }
160 :
161 2 : static void *query_wbc_thread(void *ptr)
162 : {
163 2 : struct thread_state *state = ptr;
164 : struct passwd *ppwd;
165 : wbcErr wbc_status;
166 : pid_t pid;
167 : ssize_t nread, nwritten;
168 : int p[2];
169 : int rc;
170 :
171 47176 : while (time(NULL) < state->timeout) {
172 47174 : wbc_status = wbcGetpwnam(state->username, &ppwd);
173 47174 : if (!WBC_ERROR_IS_OK(wbc_status)) {
174 0 : pthread_mutex_lock(&state->lock);
175 0 : state->fail = true;
176 0 : pthread_mutex_unlock(&state->lock);
177 0 : fprintf(stderr,
178 : "wbcGetpwnam failed with %s\n",
179 : wbcErrorString(wbc_status));
180 0 : break;
181 : }
182 47174 : wbcFreeMemory(ppwd);
183 47174 : state->wbc_loop_count++;
184 47174 : pthread_mutex_lock(&state->lock);
185 47174 : if (state->fail) {
186 0 : pthread_mutex_unlock(&state->lock);
187 0 : break;
188 : }
189 47174 : pthread_mutex_unlock(&state->lock);
190 : }
191 :
192 2 : rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
193 2 : if (rc != 0) {
194 0 : state->fail = true;
195 0 : return NULL;
196 : }
197 :
198 : /*
199 : * Check wbcGetpwnam() still works after a fork,
200 : * both in parent and child.
201 : */
202 :
203 2 : pid = fork();
204 4 : if (pid == -1) {
205 0 : return NULL;
206 : }
207 4 : if (pid == 0) {
208 : /* Child */
209 2 : wbc_status = wbcGetpwnam(state->username, &ppwd);
210 2 : if (!WBC_ERROR_IS_OK(wbc_status)) {
211 0 : fprintf(stderr,
212 : "wbcGetpwnam failed with %s\n",
213 : wbcErrorString(wbc_status));
214 0 : rc = 1;
215 0 : nwritten = write(p[0], &rc, sizeof(int));
216 0 : assert(nwritten == sizeof(int));
217 0 : exit(1);
218 : }
219 2 : wbcFreeMemory(ppwd);
220 2 : printf("child: wbcGetpwnam in child succeeded\n");
221 2 : rc = 0;
222 2 : nwritten = write(p[0], &rc, sizeof(int));
223 2 : assert(nwritten == sizeof(int));
224 2 : exit(1);
225 : }
226 :
227 : /* Parent */
228 :
229 : /* Check result from child */
230 2 : nread = read(p[1], &rc, sizeof(int));
231 2 : if (nread != sizeof(int)) {
232 0 : fprintf(stderr,
233 : "read from child failed with errno='%s' nread=%zd\n",
234 0 : strerror(errno),
235 : nread);
236 0 : state->fail = true;
237 0 : return NULL;
238 : }
239 :
240 2 : if (rc != 0) {
241 0 : fprintf(stderr,
242 : "wbcGetpwnam failed in the child\n");
243 0 : state->fail = true;
244 0 : return NULL;
245 : }
246 2 : printf("parent: wbcGetpwnam in child succeeded\n");
247 :
248 : /* Verify wbcGetpwnam() in parent after fork */
249 2 : wbc_status = wbcGetpwnam(state->username, &ppwd);
250 2 : if (!WBC_ERROR_IS_OK(wbc_status)) {
251 0 : fprintf(stderr,
252 : "wbcGetpwnam failed with %s\n",
253 : wbcErrorString(wbc_status));
254 0 : state->fail = true;
255 0 : return NULL;
256 : }
257 2 : wbcFreeMemory(ppwd);
258 2 : printf("parent: wbcGetpwnam in parent succeeded\n");
259 2 : return NULL;
260 : }
261 :
262 2 : int main(int argc, char *argv[])
263 : {
264 : int rc, n;
265 : struct thread_state state;
266 : pthread_t threads[2];
267 :
268 2 : if (argc < 2 ) {
269 0 : fprintf(stderr,"%s: missing domain user\n", argv[0]);
270 0 : return 1;
271 : }
272 :
273 2 : state.username = argv[1];
274 2 : state.timeout = time(NULL) + RUNTIME;
275 2 : rc = pthread_mutex_init(&state.lock, NULL);
276 2 : if (rc != 0) {
277 0 : fprintf(stderr,
278 : "pthread_mutex_init failed: %s\n",
279 : strerror(rc));
280 0 : exit(1);
281 : }
282 2 : state.fail = false;
283 2 : state.nss_loop_count = 0;
284 2 : state.wbc_loop_count = 0;
285 :
286 2 : printf("query domain user '%s'\n", state.username);
287 :
288 : /* create query threads */
289 2 : rc = pthread_create(&threads[0], NULL, query_nss_thread, &state);
290 2 : if (rc != 0) {
291 0 : fprintf(stderr,
292 : "creating NSS thread failed: %s\n",
293 : strerror(rc));
294 0 : exit(1);
295 : }
296 2 : rc = pthread_create(&threads[1], NULL, query_wbc_thread, &state);
297 2 : if (rc != 0) {
298 0 : fprintf(stderr,
299 : "creating libwbclient thread failed: %s\n",
300 : strerror(rc));
301 0 : exit(1);
302 : }
303 :
304 : /* wait for query threads to terminate */
305 6 : for (n = 0; n < 2; n++) {
306 4 : rc = pthread_join(threads[n], NULL);
307 4 : if (rc != 0) {
308 0 : fprintf(stderr,
309 : "joining query thread %i failed: %s\n",
310 : n,
311 : strerror(rc));
312 0 : exit(1);
313 : }
314 : }
315 :
316 2 : fprintf(state.fail ? stderr: stdout,
317 : "test %s with %i NSS and %i libwbclient calls\n",
318 2 : state.fail ? "failed" : "passed",
319 : state.nss_loop_count,
320 : state.wbc_loop_count);
321 :
322 2 : return state.fail;
323 : }
|