lib: Add fdopen_keepfd()
[samba.git] / nsswitch / stress-nss-libwbclient.c
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 static void *query_nss_thread(void *ptr)
49 {
50         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         while (time(NULL) < state->timeout) {
59                 rc = getpwnam_r(state->username,
60                                 &pwd,
61                                 buf,
62                                 sizeof(buf),
63                                 &result);
64                 if (rc != 0 || result == NULL) {
65                         pthread_mutex_lock(&state->lock);
66                         state->fail = true;
67                         pthread_mutex_unlock(&state->lock);
68                         fprintf(stderr,
69                                 "getpwnam_r failed with rc='%s' result=%p\n",
70                                 strerror(rc),
71                                 result);
72                         break;
73                 }
74                 state->nss_loop_count++;
75                 pthread_mutex_lock(&state->lock);
76                 if (state->fail) {
77                         pthread_mutex_unlock(&state->lock);
78                         break;
79                 }
80                 pthread_mutex_unlock(&state->lock);
81         }
82
83         rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
84         if (rc != 0) {
85                 state->fail = true;
86                 return NULL;
87         }
88
89         /*
90          * Check getpwnam_r() still works after a fork,
91          * both in parent and child.
92          */
93
94         pid = fork();
95         if (pid == -1) {
96                 return NULL;
97         }
98         if (pid == 0) {
99                 /* Child */
100                 rc = getpwnam_r(state->username,
101                                 &pwd,
102                                 buf,
103                                 sizeof(buf),
104                                 &result);
105                 if (rc != 0 || result == NULL) {
106                         fprintf(stderr,
107                                 "getpwnam_r failed with rc='%s' result=%p\n",
108                                 strerror(rc),
109                                 result);
110                         rc = 1;
111                         nwritten = write(p[0], &rc, sizeof(int));
112                         assert(nwritten == sizeof(int));
113                         exit(1);
114                 }
115                 printf("child: getpwnam_r in child succeeded\n");
116                 rc = 0;
117                 nwritten = write(p[0], &rc, sizeof(int));
118                 assert(nwritten == sizeof(int));
119                 exit(1);
120         }
121
122         /* Parent */
123
124         /* Check result from child */
125         nread = read(p[1], &rc, sizeof(int));
126         if (nread != sizeof(int)) {
127                 fprintf(stderr,
128                         "read from child failed with errno='%s' nread=%zd\n",
129                         strerror(errno),
130                         nread);
131                 state->fail = true;
132                 return NULL;
133         }
134
135         if (rc != 0) {
136                 fprintf(stderr,
137                         "getpwnam_r failed in the child\n");
138                 state->fail = true;
139                 return NULL;
140         }
141         printf("parent: getpwnam_r in child succeeded\n");
142
143         /* Verify getpwnam_r() in parent after fork */
144         rc = getpwnam_r(state->username,
145                         &pwd,
146                         buf,
147                         sizeof(buf),
148                         &result);
149         if (rc != 0 || result == NULL) {
150                 fprintf(stderr,
151                         "getpwnam_r failed with rc='%s' result=%p\n",
152                         strerror(rc),
153                         result);
154                 state->fail = true;
155                 return NULL;
156         }
157         printf("parent: getpwnam_r in parent succeeded\n");
158         return NULL;
159 }
160
161 static void *query_wbc_thread(void *ptr)
162 {
163         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         while (time(NULL) < state->timeout) {
172                 wbc_status = wbcGetpwnam(state->username, &ppwd);
173                 if (!WBC_ERROR_IS_OK(wbc_status)) {
174                         pthread_mutex_lock(&state->lock);
175                         state->fail = true;
176                         pthread_mutex_unlock(&state->lock);
177                         fprintf(stderr,
178                                 "wbcGetpwnam failed with %s\n",
179                                 wbcErrorString(wbc_status));
180                         break;
181                 }
182                 wbcFreeMemory(ppwd);
183                 state->wbc_loop_count++;
184                 pthread_mutex_lock(&state->lock);
185                 if (state->fail) {
186                         pthread_mutex_unlock(&state->lock);
187                         break;
188                 }
189                 pthread_mutex_unlock(&state->lock);
190         }
191
192         rc = socketpair(AF_UNIX, SOCK_STREAM, 0, p);
193         if (rc != 0) {
194                 state->fail = true;
195                 return NULL;
196         }
197
198         /*
199          * Check wbcGetpwnam() still works after a fork,
200          * both in parent and child.
201          */
202
203         pid = fork();
204         if (pid == -1) {
205                 return NULL;
206         }
207         if (pid == 0) {
208                 /* Child */
209                 wbc_status = wbcGetpwnam(state->username, &ppwd);
210                 if (!WBC_ERROR_IS_OK(wbc_status)) {
211                         fprintf(stderr,
212                                 "wbcGetpwnam failed with %s\n",
213                                 wbcErrorString(wbc_status));
214                         rc = 1;
215                         nwritten = write(p[0], &rc, sizeof(int));
216                         assert(nwritten == sizeof(int));
217                         exit(1);
218                 }
219                 wbcFreeMemory(ppwd);
220                 printf("child: wbcGetpwnam in child succeeded\n");
221                 rc = 0;
222                 nwritten = write(p[0], &rc, sizeof(int));
223                 assert(nwritten == sizeof(int));
224                 exit(1);
225         }
226
227         /* Parent */
228
229         /* Check result from child */
230         nread = read(p[1], &rc, sizeof(int));
231         if (nread != sizeof(int)) {
232                 fprintf(stderr,
233                         "read from child failed with errno='%s' nread=%zd\n",
234                         strerror(errno),
235                         nread);
236                 state->fail = true;
237                 return NULL;
238         }
239
240         if (rc != 0) {
241                 fprintf(stderr,
242                         "wbcGetpwnam failed in the child\n");
243                 state->fail = true;
244                 return NULL;
245         }
246         printf("parent: wbcGetpwnam in child succeeded\n");
247
248         /* Verify wbcGetpwnam() in parent after fork */
249         wbc_status = wbcGetpwnam(state->username, &ppwd);
250         if (!WBC_ERROR_IS_OK(wbc_status)) {
251                 fprintf(stderr,
252                         "wbcGetpwnam failed with %s\n",
253                         wbcErrorString(wbc_status));
254                 state->fail = true;
255                 return NULL;
256         }
257         wbcFreeMemory(ppwd);
258         printf("parent: wbcGetpwnam in parent succeeded\n");
259         return NULL;
260 }
261
262 int main(int argc, char *argv[])
263 {
264         int rc, n;
265         struct thread_state state;
266         pthread_t threads[2];
267
268         if (argc < 2 ) {
269                 fprintf(stderr,"%s: missing domain user\n", argv[0]);
270                 return 1;
271         }
272
273         state.username = argv[1];
274         state.timeout = time(NULL) + RUNTIME;
275         rc = pthread_mutex_init(&state.lock, NULL);
276         if (rc != 0) {
277                 fprintf(stderr,
278                         "pthread_mutex_init failed: %s\n",
279                         strerror(rc));
280                 exit(1);
281         }
282         state.fail = false;
283         state.nss_loop_count = 0;
284         state.wbc_loop_count = 0;
285
286         printf("query domain user '%s'\n", state.username);
287
288         /* create query threads */
289         rc = pthread_create(&threads[0], NULL, query_nss_thread, &state);
290         if (rc != 0) {
291                 fprintf(stderr,
292                         "creating NSS thread failed: %s\n",
293                         strerror(rc));
294                 exit(1);
295         }
296         rc = pthread_create(&threads[1], NULL, query_wbc_thread, &state);
297         if (rc != 0) {
298                 fprintf(stderr,
299                         "creating libwbclient thread failed: %s\n",
300                         strerror(rc));
301                 exit(1);
302         }
303
304         /* wait for query threads to terminate */
305         for (n = 0; n < 2; n++) {
306                 rc = pthread_join(threads[n], NULL);
307                 if (rc != 0) {
308                         fprintf(stderr,
309                                 "joining query thread %i failed: %s\n",
310                                 n,
311                                 strerror(rc));
312                         exit(1);
313                 }
314         }
315
316         fprintf(state.fail ? stderr: stdout,
317                 "test %s with %i NSS and %i libwbclient calls\n",
318                 state.fail ? "failed" : "passed",
319                 state.nss_loop_count,
320                 state.wbc_loop_count);
321
322         return state.fail;
323 }