Line data Source code
1 : /*
2 : SSSD
3 :
4 : Stress tests
5 :
6 : Copyright (C) Jakub Hrozek <jhrozek@redhat.com> 2009
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 <signal.h>
23 : #include <stdlib.h>
24 : #include <talloc.h>
25 : #include <popt.h>
26 : #include <sys/types.h>
27 : #include <sys/wait.h>
28 : #include <pwd.h>
29 : #include <grp.h>
30 : #include <errno.h>
31 :
32 : #include "util/util.h"
33 : #include "tests/common.h"
34 :
35 : #define DEFAULT_START 10
36 : #define DEFAULT_STOP 20
37 :
38 : #define NAME_SIZE 255
39 : #define CHUNK 64
40 :
41 :
42 : /* How many tests failed */
43 : int failure_count;
44 :
45 : /* Be chatty */
46 : int verbose;
47 :
48 : /*
49 : * Look up one user. If the user is not found using getpwnam, the success
50 : * or failure depends on enoent_fail being set.
51 : */
52 0 : int test_lookup_user(const char *name, int enoent_fail)
53 : {
54 0 : struct passwd *pwd = NULL;
55 0 : int ret = 0;
56 : int error;
57 :
58 0 : errno = 0;
59 0 : pwd = getpwnam(name);
60 0 : error = errno;
61 0 : if (pwd == NULL) {
62 0 : if (error == 0 || error == ENOENT) {
63 0 : ret = (enoent_fail == 1) ? ENOENT : 0;
64 : }
65 : }
66 :
67 0 : if (ret != 0 && verbose) {
68 0 : fprintf(stderr,
69 : "getpwnam failed (name: %s): errno = %d, error = %s\n",
70 : name, ret, strerror(ret));
71 : }
72 :
73 0 : return ret;
74 : }
75 :
76 : /*
77 : * Look up one group. If the user is not found using getgrnam, the success
78 : * or failure depends on enoent_fail being set.
79 : */
80 0 : int test_lookup_group(const char *name, int enoent_fail)
81 : {
82 0 : struct group *grp = NULL;
83 0 : int ret = 0;
84 :
85 0 : errno = 0;
86 0 : grp = getgrnam(name);
87 0 : if (grp == NULL) {
88 0 : if (errno == 0 || errno == ENOENT) {
89 0 : ret = enoent_fail ? ENOENT : 0;
90 : }
91 : }
92 :
93 0 : if (ret != 0 && verbose) {
94 0 : fprintf(stderr,
95 : "getgrnam failed (name %s): errno = %d, error = %s\n",
96 : name, ret, strerror(ret));
97 : }
98 :
99 0 : return ret;
100 : }
101 :
102 0 : int run_one_testcase(const char *name, int group, int enoent_fail)
103 : {
104 0 : if (group) {
105 0 : return test_lookup_group(name, enoent_fail);
106 : } else {
107 0 : return test_lookup_user(name, enoent_fail);
108 : }
109 : }
110 :
111 : /*
112 : * Beware, has side-effects: changes global variable failure_count
113 : */
114 0 : void child_handler(int signum)
115 : {
116 : int status, ret;
117 :
118 0 : while ((ret = wait(&status)) > 0) {
119 0 : if (ret == -1) {
120 0 : perror("wait");
121 0 : exit(EXIT_FAILURE);
122 : }
123 :
124 0 : if (WIFEXITED(status)) {
125 0 : ret = WEXITSTATUS(status);
126 0 : if (ret) {
127 0 : if (verbose) {
128 0 : fprintf(stderr,
129 : "A child exited with error code %d\n",
130 0 : WEXITSTATUS(status));
131 : }
132 0 : ++failure_count;
133 : }
134 0 : } else ++failure_count;
135 : }
136 0 : }
137 :
138 0 : int generate_names(TALLOC_CTX *mem_ctx, const char *prefix,
139 : int start, int stop, char ***_out)
140 : {
141 : char **out;
142 0 : int num_names = stop-start+1;
143 0 : int idx = 0;
144 :
145 0 : out = talloc_array(mem_ctx, char *, num_names+1);
146 0 : if (out == NULL) {
147 0 : return ENOMEM;
148 : }
149 :
150 0 : for (idx = 0; idx < num_names; ++idx) {
151 0 : out[idx] = talloc_asprintf(mem_ctx, "%s%d", prefix, idx);
152 0 : if (out[idx] == NULL) {
153 0 : return ENOMEM;
154 : }
155 : }
156 0 : out[idx] = NULL;
157 :
158 0 : *_out = out;
159 0 : return EOK;
160 : }
161 :
162 0 : int read_names(TALLOC_CTX *mem_ctx, FILE *stream, char ***_out)
163 : {
164 : char one_name[NAME_SIZE];
165 0 : int n = 0;
166 0 : int array_size = CHUNK;
167 : int ret;
168 : char **out;
169 :
170 0 : out = talloc_array(mem_ctx, char *, CHUNK+1);
171 0 : if (out == NULL) {
172 0 : return ENOMEM;
173 : }
174 0 : while (fgets(one_name, NAME_SIZE, stream)) {
175 0 : out[n] = talloc_strdup(mem_ctx, one_name);
176 0 : if (out[n] == NULL) {
177 0 : return ENOMEM;
178 : }
179 0 : if ((n++ % CHUNK) == 0) {
180 0 : array_size += CHUNK;
181 0 : out = talloc_realloc(mem_ctx, out, char *, array_size);
182 0 : if (out == NULL) {
183 0 : return ENOMEM;
184 : }
185 : }
186 : }
187 :
188 0 : if ((ret = ferror(stream))) {
189 0 : return ret;
190 : }
191 0 : out[n] = NULL;
192 :
193 0 : *_out = out;
194 0 : return EOK;
195 : }
196 :
197 0 : int main(int argc, const char *argv[])
198 : {
199 : int opt;
200 : poptContext pc;
201 0 : int pc_start=DEFAULT_START;
202 0 : int pc_stop=DEFAULT_STOP;
203 0 : int pc_enoent_fail=0;
204 0 : int pc_groups=0;
205 0 : int pc_verbosity = 0;
206 0 : char *pc_prefix = NULL;
207 0 : TALLOC_CTX *ctx = NULL;
208 0 : char **names = NULL;
209 :
210 : int status, idx, ret;
211 : pid_t pid;
212 : struct sigaction action, old_action;
213 :
214 0 : struct poptOption long_options[] = {
215 : POPT_AUTOHELP
216 : { "groups", 'g', POPT_ARG_NONE, &pc_groups, 0,
217 : "Lookup in groups instead of users", NULL },
218 : { "prefix", '\0', POPT_ARG_STRING, &pc_prefix, 0,
219 : "The username prefix", NULL },
220 : { "start", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
221 : &pc_start, 0,
222 : "Start value to append to prefix", NULL },
223 : { "stop", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT,
224 : &pc_stop, 0,
225 : "End value to append to prefix", NULL },
226 : { "enoent-fail", '\0', POPT_ARG_NONE, &pc_enoent_fail, 0,
227 : "Fail on not getting the requested NSS data (default: No)",
228 : NULL },
229 : { "verbose", 'v', POPT_ARG_NONE, 0, 'v',
230 : "Be verbose", NULL },
231 : POPT_TABLEEND
232 : };
233 :
234 : /* parse the params */
235 0 : pc = poptGetContext(argv[0], argc, argv, long_options, 0);
236 0 : while ((opt = poptGetNextOpt(pc)) != -1) {
237 0 : switch (opt) {
238 : case 'v':
239 0 : pc_verbosity = 1;
240 0 : break;
241 :
242 : default:
243 0 : fprintf(stderr, "\nInvalid option %s: %s\n\n",
244 : poptBadOption(pc, 0), poptStrerror(opt));
245 0 : poptPrintUsage(pc, stderr, 0);
246 0 : return 1;
247 : }
248 : }
249 0 : poptFreeContext(pc);
250 :
251 0 : tests_set_cwd();
252 :
253 0 : verbose = pc_verbosity;
254 :
255 0 : if (pc_prefix) {
256 0 : ret = generate_names(ctx, pc_prefix, pc_start, pc_stop, &names);
257 0 : if (ret != EOK) {
258 0 : if (verbose) {
259 0 : errno = ret;
260 0 : perror("generate_names");
261 : }
262 0 : exit(EXIT_FAILURE);
263 : }
264 : } else {
265 0 : ret = read_names(ctx, stdin, &names);
266 0 : if (ret != EOK) {
267 0 : if (verbose) {
268 0 : errno = ret;
269 0 : perror("read_names");
270 : }
271 0 : exit(EXIT_FAILURE);
272 : }
273 : }
274 :
275 : /* Reap the children in a handler asynchronously so we can
276 : * somehow protect against too many processes */
277 0 : memset(&action, 0, sizeof(action));
278 0 : action.sa_handler = child_handler;
279 0 : sigemptyset(&action.sa_mask);
280 0 : sigaddset(&action.sa_mask, SIGCHLD);
281 0 : action.sa_flags = SA_NOCLDSTOP;
282 :
283 0 : sigaction(SIGCHLD, &action, &old_action);
284 :
285 : /* Fire up the child processes */
286 0 : idx = 0;
287 0 : for (idx=0; names[idx]; idx++) {
288 0 : pid = fork();
289 0 : if (pid == -1) {
290 : /* Try again in hope that some child has exited */
291 0 : if (errno == EAGAIN) {
292 0 : continue;
293 : }
294 0 : perror("fork");
295 0 : exit(EXIT_FAILURE);
296 0 : } else if ( pid == 0 ) {
297 : /* child */
298 0 : ret = run_one_testcase(names[idx], pc_groups, pc_enoent_fail);
299 0 : exit(ret);
300 : }
301 : }
302 :
303 : /* Process the rest of the children here in main */
304 0 : sigaction(SIGCHLD, &old_action, NULL);
305 0 : while ((ret = wait(&status)) > 0) {
306 0 : if (ret == -1) {
307 0 : perror("wait");
308 0 : exit(EXIT_FAILURE);
309 : }
310 :
311 0 : if (WIFEXITED(status)) {
312 0 : ret = WEXITSTATUS(status);
313 0 : if (ret) {
314 0 : if (verbose) {
315 0 : fprintf(stderr,
316 : "A child exited with error code %d\n",
317 0 : WEXITSTATUS(status));
318 : }
319 0 : ++failure_count;
320 : }
321 0 : } else ++failure_count;
322 : }
323 :
324 0 : if (pc_verbosity) {
325 0 : fprintf(stderr,
326 : "Total tests run: %d\nPassed: %d\nFailed: %d\n",
327 : idx,
328 : idx - failure_count,
329 : failure_count);
330 : }
331 0 : return (failure_count==0 ? EXIT_SUCCESS : EXIT_FAILURE);
332 : }
|