Commit | Line | Data |
---|---|---|
805e021f CE |
1 | /* |
2 | * Copyright 2000, International Business Machines Corporation and others. | |
3 | * All Rights Reserved. | |
4 | * | |
5 | * This software has been released under the terms of the IBM Public | |
6 | * License. For details, see the LICENSE file in the top-level source | |
7 | * directory or online at http://www.openafs.org/dl/license10.html | |
8 | */ | |
9 | ||
10 | #include <afsconfig.h> | |
11 | #include <afs/param.h> | |
12 | ||
13 | #include <roken.h> | |
14 | #include <afs/opr.h> | |
15 | ||
16 | #include <ctype.h> | |
17 | #include <math.h> | |
18 | ||
19 | #ifdef AFS_NT40_ENV | |
20 | #include <WINNT/afsevent.h> | |
21 | #endif | |
22 | ||
23 | #include <rx/rx.h> | |
24 | #include <rx/xdr.h> | |
25 | #include <afs/rxgen_consts.h> | |
26 | #include <afs/cmd.h> | |
27 | #include <afs/auth.h> | |
28 | #include <afs/cellconfig.h> | |
29 | #include <afs/afsutil.h> | |
30 | #include <afs/com_err.h> | |
31 | ||
32 | #include "ptclient.h" | |
33 | #include "pterror.h" | |
34 | #include "ptuser.h" | |
35 | #include "ptprototypes.h" | |
36 | ||
37 | static char *whoami = "testpr"; | |
38 | static struct afsconf_dir *conf; /* cell info, set by MyBeforeProc */ | |
39 | static char conf_dir[100]; | |
40 | static char lcell[MAXCELLCHARS]; | |
41 | ||
42 | int | |
43 | ListUsedIds(struct cmd_syndesc *as, void *arock) | |
44 | { | |
45 | afs_int32 code; | |
46 | namelist lnames; | |
47 | idlist lids; | |
48 | int i, j; | |
49 | int group = 0; /* check groups */ | |
50 | int unused = 0; /* print unused */ | |
51 | int number = 100; /* check 100 ids */ | |
52 | afs_int32 startId = 1; | |
53 | afs_int32 maxId; | |
54 | int range; | |
55 | ||
56 | if (as->parms[0].items) | |
57 | startId = atoi(as->parms[0].items->data); | |
58 | if (as->parms[1].items) | |
59 | number = atoi(as->parms[1].items->data); | |
60 | if (as->parms[2].items) | |
61 | unused = 1; | |
62 | ||
63 | code = pr_Initialize(1, conf_dir, NULL); | |
64 | if (code) { | |
65 | afs_com_err(whoami, code, "initializing pruser"); | |
66 | exit(1); | |
67 | } | |
68 | if (startId < 0) { | |
69 | group = 1; | |
70 | code = pr_ListMaxGroupId(&maxId); | |
71 | if (code) { | |
72 | bad_max: | |
73 | afs_com_err(whoami, code, "getting maximum id"); | |
74 | exit(2); | |
75 | } | |
76 | if (startId < maxId) { | |
77 | fprintf(stderr, "Max group id is only %d.\n", maxId); | |
78 | exit(3); | |
79 | } | |
80 | } else { | |
81 | code = pr_ListMaxUserId(&maxId); | |
82 | if (code) | |
83 | goto bad_max; | |
84 | if (startId > maxId) { | |
85 | fprintf(stderr, "Max user id is only %d.\n", maxId); | |
86 | exit(3); | |
87 | } | |
88 | } | |
89 | range = abs(startId - maxId); | |
90 | range++; /* number that can be printed */ | |
91 | if (range < number) { | |
92 | fprintf(stderr, "Only %d ids to be checked.\n", range); | |
93 | number = range; | |
94 | } | |
95 | ||
96 | printf("Checking for %d %sused ids starting at %d.\n", number, | |
97 | (unused ? "un" : ""), startId); | |
98 | #define NUM 100 | |
99 | lids.idlist_val = malloc(sizeof(afs_int32) * NUM); | |
100 | lnames.namelist_len = 0; | |
101 | lnames.namelist_val = 0; | |
102 | while (number) { | |
103 | if (number < NUM) | |
104 | i = number; | |
105 | else | |
106 | i = NUM; | |
107 | for (j = 0; j < i; j++) { | |
108 | lids.idlist_val[j] = startId; | |
109 | if (group) | |
110 | startId--; | |
111 | else | |
112 | startId++; | |
113 | } | |
114 | lids.idlist_len = i; | |
115 | code = pr_IdToName(&lids, &lnames); | |
116 | if (code) { | |
117 | afs_com_err(whoami, code, "converting id to name"); | |
118 | exit(2); | |
119 | } | |
120 | for (j = 0; j < lnames.namelist_len; j++) { | |
121 | if (lids.idlist_val[j] == atoi(lnames.namelist_val[j])) { | |
122 | if (unused) | |
123 | printf("%s is free\n", lnames.namelist_val[j]); | |
124 | } else { | |
125 | if (!unused) | |
126 | printf("%s is id %d\n", lnames.namelist_val[j], | |
127 | lids.idlist_val[j]); | |
128 | } | |
129 | } | |
130 | number -= i; | |
131 | } | |
132 | if (lids.idlist_val) | |
133 | free(lids.idlist_val); | |
134 | if (lnames.namelist_val) | |
135 | free(lnames.namelist_val); | |
136 | return 0; | |
137 | } | |
138 | ||
139 | /* TestManyMembers - called with a number N. Try creating N users and N groups | |
140 | * and put all the users on one of the groups and one of the users on all the | |
141 | * groups. Also put many users on many groups. | |
142 | * | |
143 | * To keep track of this create an NxN matrix of membership and fill it in with | |
144 | * a function that looks like a quarter of a circle. That makes the first | |
145 | * group contain every user and the first user be a member of every group. */ | |
146 | ||
147 | int verbose; | |
148 | char callerName[PR_MAXNAMELEN]; | |
149 | afs_int32 callerId; | |
150 | afs_int32 lastGroup; /* id of last group created */ | |
151 | afs_int32 ownerUser; /* first created user */ | |
152 | char ownerUserName[PR_MAXNAMELEN]; /* " " " name */ | |
153 | int steepDropOff; /* precentage decreate in GroupLimit */ | |
154 | char *createPrefix; /* prefix for naming users&groups */ | |
155 | extern struct ubik_client *pruclient; /* initialized by pr_Initialize */ | |
156 | ||
157 | /* These variables form the state if this test */ | |
158 | int number; /* max number of members */ | |
159 | char *population; /* matrix of memberships */ | |
160 | afs_int32 *users; /* ids of users */ | |
161 | afs_int32 *groups; /* ids of groups */ | |
162 | afs_int32 *groupOwners; /* ids of owners of groups */ | |
163 | ||
164 | /* statistics */ | |
165 | int nUsers, nGroups, nAdds, nRems, nUDels, nGDels; | |
166 | ||
167 | int | |
168 | IdCmp(const void *a, const void *b) | |
169 | { | |
170 | if (*(afs_int32 *)a > *(afs_int32 *)b) { | |
171 | return 1; | |
172 | } else if (*(afs_int32 *)a == *(afs_int32 *)b) { | |
173 | return 0; | |
174 | } else /* (*a < *b) */ { | |
175 | return -1; | |
176 | } | |
177 | } | |
178 | ||
179 | static int | |
180 | sqr(int n) | |
181 | { | |
182 | return n * n; | |
183 | } | |
184 | ||
185 | static int | |
186 | GetGroupLimit(int N, int x) | |
187 | { | |
188 | int y; | |
189 | ||
190 | if ((x >= N) || (x < 0)) { | |
191 | printf("GetGroupLimit: input value out of range %d (%d)\n", x, N); | |
192 | exit(10); | |
193 | } | |
194 | if (steepDropOff) { /* Use exponential decrease */ | |
195 | int i; | |
196 | y = N; | |
197 | for (i = 0; i < x; i++) { | |
198 | y = (y * steepDropOff) / 100; /* parameter is a percentage */ | |
199 | if (y == 0) { | |
200 | y = 1; /* with a floor of 1 */ | |
201 | break; | |
202 | } | |
203 | } | |
204 | } else { /* Use a circle's third quadrant */ | |
205 | y = sqr(N - 1) - sqr(N - 1 - x); | |
206 | y = (int)(sqrt((double)y) + 0.5); /* round off */ | |
207 | y = N - y; | |
208 | } | |
209 | if ((y > N) || (y < 1)) { | |
210 | printf("filling value out of range: %d (%d) => %d\n", x, N, y); | |
211 | exit(11); | |
212 | } | |
213 | return y; | |
214 | } | |
215 | ||
216 | void | |
217 | CreateUser(int u) | |
218 | { | |
219 | afs_int32 code; | |
220 | char name[16]; | |
221 | afs_int32 id; | |
222 | ||
223 | sprintf(name, "%s%d", createPrefix, u); | |
224 | id = 0; | |
225 | code = pr_CreateUser(name, &id); | |
226 | if (code) { | |
227 | if (code == PREXIST) { | |
228 | code = pr_Delete(name); | |
229 | if (code == 0) { | |
230 | nUDels++; | |
231 | code = pr_CreateUser(name, &id); | |
232 | if (code == 0) { | |
233 | if (verbose) | |
234 | printf("RE-"); | |
235 | goto done; | |
236 | } | |
237 | } | |
238 | } | |
239 | afs_com_err(whoami, code, "couldn't create %s", name); | |
240 | exit(12); | |
241 | } | |
242 | done: | |
243 | if (verbose) | |
244 | printf("Creating user %s (%di)\n", name, id); | |
245 | users[u] = id; | |
246 | nUsers++; | |
247 | ||
248 | if (ownerUser == 0) { | |
249 | ownerUser = id; | |
250 | strcpy(ownerUserName, name); | |
251 | } | |
252 | } | |
253 | ||
254 | void | |
255 | CreateGroup(int g) | |
256 | { | |
257 | afs_int32 code; | |
258 | char name[16]; | |
259 | afs_int32 id = 0; | |
260 | afs_int32 owner = 0; | |
261 | char *ownerName = NULL; | |
262 | int ownerType; /* type of ownership */ | |
263 | static char *lastGroupPrefix; /* prefix used for type==2 */ | |
264 | ||
265 | /* At least 50 groups should be owned by another group to test long owner | |
266 | * chains during deletion. Also let's create some long owners of owners | |
267 | * lists. */ | |
268 | ownerType = random() % 3; | |
269 | ||
270 | if (!ownerUser) | |
271 | ownerType = 0; | |
272 | if (!lastGroup) | |
273 | ownerType = 0; | |
274 | switch (ownerType) { | |
275 | case 0: | |
276 | owner = callerId; | |
277 | ownerName = callerName; | |
278 | break; | |
279 | case 1: | |
280 | owner = ownerUser; | |
281 | ownerName = ownerUserName; | |
282 | break; | |
283 | case 2: | |
284 | owner = lastGroup; | |
285 | ownerName = lastGroupPrefix; | |
286 | break; | |
287 | } | |
288 | ||
289 | sprintf(name, "%s:%s%d", ownerName, createPrefix, g); | |
290 | code = ubik_PR_NewEntry(pruclient, 0, name, PRGRP, owner, &id); | |
291 | if (code) { | |
292 | if (code == PREXIST) { | |
293 | code = pr_Delete(name); | |
294 | if (code == 0) { | |
295 | nGDels++; | |
296 | code = | |
297 | ubik_PR_NewEntry(pruclient, 0, name, PRGRP, owner, | |
298 | &id); | |
299 | if (code == 0) { | |
300 | if (verbose) | |
301 | printf("RE-"); | |
302 | goto done; | |
303 | } | |
304 | } | |
305 | } | |
306 | afs_com_err(whoami, code, "couldn't create %s w/ owner=%d", name, owner); | |
307 | exit(13); | |
308 | } | |
309 | done: | |
310 | if (verbose) | |
311 | printf("Creating group %s (%di)\n", name, id); | |
312 | groups[g] = id; | |
313 | groupOwners[g] = owner; | |
314 | nGroups++; | |
315 | if (!lastGroup || (ownerType == 2)) { | |
316 | lastGroup = id; | |
317 | lastGroupPrefix = ownerName; | |
318 | } | |
319 | } | |
320 | ||
321 | int | |
322 | DeleteRandomId(afs_int32 *list) | |
323 | { | |
324 | afs_int32 code; | |
325 | afs_int32 id; | |
326 | int j, k; | |
327 | int m; | |
328 | ||
329 | k = random(); /* random starting point */ | |
330 | for (j = 0; j < number; j++) { /* find an undeleted id */ | |
331 | m = (k + j) % number; | |
332 | if ((id = list[m])) { | |
333 | code = ubik_PR_Delete(pruclient, 0, id); | |
334 | if (code) { | |
335 | afs_com_err(whoami, code, "Couldn't delete %di", id); | |
336 | exit(22); | |
337 | } | |
338 | list[m] = 0; | |
339 | if (list == users) | |
340 | nUDels++; | |
341 | else | |
342 | nGDels++; | |
343 | return 0; | |
344 | } | |
345 | } | |
346 | return -1; /* none left */ | |
347 | } | |
348 | ||
349 | void | |
350 | AddUser(int u, int g) | |
351 | { | |
352 | afs_int32 code; | |
353 | afs_int32 ui, gi; | |
354 | ||
355 | if (users[u] == 0) /* create if necessary */ | |
356 | CreateUser(u); | |
357 | if (groups[g] == 0) /* create group if necessary */ | |
358 | CreateGroup(g); | |
359 | ui = users[u]; | |
360 | gi = groups[g]; | |
361 | code = ubik_PR_AddToGroup(pruclient, 0, ui, gi); | |
362 | if (code) { | |
363 | afs_com_err(whoami, code, "couldn't add %d to %d", ui, gi); | |
364 | exit(14); | |
365 | } | |
366 | if (verbose) | |
367 | printf("Adding user (%di) to group (%di)\n", ui, gi); | |
368 | population[u * number + g]++; | |
369 | nAdds++; | |
370 | } | |
371 | ||
372 | void | |
373 | RemUser(int u, int g) | |
374 | { | |
375 | afs_int32 code; | |
376 | afs_int32 ui, gi; | |
377 | ||
378 | ui = users[u]; | |
379 | gi = groups[g]; | |
380 | code = ubik_PR_RemoveFromGroup(pruclient, 0, ui, gi); | |
381 | if (code) { | |
382 | afs_com_err(whoami, code, "couldn't remove %d from %d", ui, gi); | |
383 | exit(14); | |
384 | } | |
385 | if (verbose) | |
386 | printf("Removing user (%di) from group (%di)\n", ui, gi); | |
387 | population[u * number + g]--; | |
388 | nRems++; | |
389 | } | |
390 | ||
391 | int | |
392 | TestManyMembers(struct cmd_syndesc *as, void *arock) | |
393 | { | |
394 | char *filled; /* users filled up */ | |
395 | char *cleaned; /* users cleaned up */ | |
396 | ||
397 | int nFilled, nCleaned; | |
398 | int u, g, i, j, n; | |
399 | int seed; /* random number generator seed */ | |
400 | ||
401 | afs_int32 *glist; /* membership list */ | |
402 | ||
403 | afs_int32 code; | |
404 | ||
405 | code = pr_Initialize(1, conf_dir, NULL); | |
406 | if (code) { | |
407 | afs_com_err(whoami, code, "initializing pruser"); | |
408 | exit(1); | |
409 | } | |
410 | /* get name of person running command */ | |
411 | { | |
412 | struct ktc_principal afs, user; | |
413 | struct ktc_token token; | |
414 | ||
415 | strcpy(afs.name, "afs"); | |
416 | strcpy(afs.instance, ""); | |
417 | code = afsconf_GetLocalCell(conf, afs.cell, sizeof(afs.cell)); | |
418 | if (code) | |
419 | exit(2); | |
420 | code = ktc_GetToken(&afs, &token, sizeof(token), &user); | |
421 | if (code) { | |
422 | afs_com_err(whoami, code, "getting afs tokens"); | |
423 | exit(3); | |
424 | } | |
425 | if (strlen(user.instance) > 0) { | |
426 | fprintf(stderr, "can't handle non-null instance %s.%s\n", | |
427 | user.name, user.cell); | |
428 | exit(4); | |
429 | } | |
430 | if (strncmp(user.name, "AFS ID ", 7) == 0) { | |
431 | callerId = atoi(user.name + 7); | |
432 | code = pr_SIdToName(callerId, callerName); | |
433 | if (code) { | |
434 | afs_com_err(whoami, code, "call get name for id %d", callerId); | |
435 | exit(6); | |
436 | } | |
437 | } else { | |
438 | strcpy(callerName, user.name); | |
439 | code = pr_SNameToId(callerName, &callerId); | |
440 | if ((code == 0) && (callerId == ANONYMOUSID)) | |
441 | code = PRNOENT; | |
442 | } | |
443 | #if 0 /* don't create user */ | |
444 | if (code == PRNOENT) { | |
445 | callerId = 0; | |
446 | code = pr_CreateUser(callerName, &callerId); | |
447 | if (code) { | |
448 | afs_com_err(whoami, code, "can't create caller %s", callerName); | |
449 | exit(5); | |
450 | } | |
451 | printf("Creating caller %s (%di)\n", callerName, callerId); | |
452 | } | |
453 | /* else */ | |
454 | #endif | |
455 | if (code) { | |
456 | afs_com_err(whoami, code, "can't find caller %s", callerName); | |
457 | exit(6); | |
458 | } else | |
459 | printf("Assuming caller is %s (%di)\n", callerName, callerId); | |
460 | } | |
461 | ||
462 | /* Parse arguments */ | |
463 | if (as->parms[0].items) | |
464 | number = atoi(as->parms[0].items->data); | |
465 | if (as->parms[1].items) { | |
466 | steepDropOff = atoi(as->parms[1].items->data); | |
467 | if ((steepDropOff < 0) || (steepDropOff > 100)) { | |
468 | fprintf(stderr, | |
469 | "Illegal value for dropoff: %d, must be between 0 and 100, inclusive.\n", | |
470 | steepDropOff); | |
471 | exit(7); | |
472 | } | |
473 | } else | |
474 | steepDropOff = 0; /* use quadratic dropoff */ | |
475 | if (as->parms[2].items) | |
476 | createPrefix = as->parms[2].items->data; | |
477 | else | |
478 | createPrefix = "u"; | |
479 | if (as->parms[3].items) | |
480 | verbose = 1; | |
481 | else | |
482 | verbose = 0; | |
483 | if (as->parms[4].items) | |
484 | seed = atoi(as->parms[4].items->data); | |
485 | else | |
486 | seed = 1; | |
487 | ||
488 | srandom(seed); | |
489 | ||
490 | users = calloc(number, sizeof(afs_int32)); | |
491 | groups = calloc(number, sizeof(afs_int32)); | |
492 | filled = calloc(number, sizeof(char)); | |
493 | cleaned = calloc(number, sizeof(char)); | |
494 | population = calloc(sqr(number), sizeof(char)); | |
495 | ||
496 | nFilled = 0; | |
497 | nCleaned = 0; | |
498 | ||
499 | ownerUser = lastGroup = 0; | |
500 | groupOwners = malloc(number * sizeof(afs_int32)); | |
501 | nUsers = nGroups = nAdds = nRems = nUDels = nGDels = 0; | |
502 | ||
503 | while ((nFilled < number) || (nCleaned < number)) { | |
504 | /* pick a user at random, using */ | |
505 | u = random() % number; | |
506 | if (!filled[u]) { | |
507 | n = GetGroupLimit(number, u); /* get group limit for that user */ | |
508 | g = random() % (n + 1); /* pick a random group */ | |
509 | if (g == n) { /* in a few cases create any user */ | |
510 | n = number; /* in the whole range */ | |
511 | g = random() % n; | |
512 | } | |
513 | for (i = 0; i < n; i++) { /* rotate until unused one found */ | |
514 | j = (g + i) % n; | |
515 | if (!population[u * number + j]) { | |
516 | /* add this user/group membership */ | |
517 | AddUser(u, j); | |
518 | goto added; | |
519 | } | |
520 | } | |
521 | filled[u]++; | |
522 | nFilled++; | |
523 | added:; | |
524 | } | |
525 | if (!cleaned[u]) { | |
526 | int base; | |
527 | if (filled[u]) { /* only clean above GroupLimit */ | |
528 | base = GetGroupLimit(number, u); | |
529 | n = number - base; | |
530 | if (n == 0) | |
531 | goto iscleaned; | |
532 | g = random() % n; | |
533 | } else { | |
534 | base = 0; | |
535 | n = number; /* pick a group from the whole range */ | |
536 | g = random() % 2 * n; /* at random for removal */ | |
537 | if (g >= n) | |
538 | goto remed; /* but half the time do nothing */ | |
539 | } | |
540 | for (i = 0; i < n; i++) { /* rotate until used one found */ | |
541 | j = (g + i) % n + base; | |
542 | if (population[u * number + j]) { | |
543 | /* remove this user/group membership */ | |
544 | RemUser(u, j); | |
545 | goto remed; | |
546 | } | |
547 | } | |
548 | if (filled[u]) { /* track finished ones */ | |
549 | iscleaned: | |
550 | cleaned[u]++; | |
551 | nCleaned++; | |
552 | } | |
553 | remed:; | |
554 | } | |
555 | } | |
556 | ||
557 | /* check the membership list of all users for correctness */ | |
558 | printf("Starting check of memberships\n"); | |
559 | glist = malloc(number * sizeof(afs_int32)); | |
560 | for (u = 0; u < number; u++) { | |
561 | afs_int32 ui = users[u]; | |
562 | if (ui) { | |
563 | int i; | |
564 | int ng; /* number groups */ | |
565 | int over; | |
566 | int (*proc)(struct ubik_client *, afs_int32, afs_int32, prlist *, | |
567 | afs_int32 *); | |
568 | prlist alist; | |
569 | ||
570 | alist.prlist_len = 0; | |
571 | alist.prlist_val = 0; | |
572 | if (random() & 4) { | |
573 | proc = ubik_PR_ListElements; | |
574 | } else { | |
575 | proc = ubik_PR_GetCPS; | |
576 | } | |
577 | code = (*proc)(pruclient, 0, ui, &alist, &over); | |
578 | if (code) { | |
579 | afs_com_err(whoami, code, | |
580 | "getting membership list of (%di) using %s", ui, | |
581 | (proc == ubik_PR_ListElements?"ListElements":"GetCPS")); | |
582 | exit(24); | |
583 | } | |
584 | if (over) { | |
585 | fprintf(stderr, "membership list for id %di too long\n", ui); | |
586 | } | |
587 | ng = 0; | |
588 | for (i = 0; i < number; i++) | |
589 | if (population[u * number + i]) | |
590 | glist[ng++] = groups[i]; | |
591 | qsort(glist, ng, sizeof(afs_int32), IdCmp); | |
592 | if (ng != (alist.prlist_len - ((proc == ubik_PR_GetCPS) ? 3 : 0))) { | |
593 | fprintf(stderr, | |
594 | "Membership list for %di of unexpected length: was %d but expected %d\n", | |
595 | ui, alist.prlist_len, ng); | |
596 | exit(20); | |
597 | } | |
598 | /* all the extra entries for the CPS should be at the end. */ | |
599 | code = 0; | |
600 | for (i = 0; i < ng; i++) | |
601 | if (alist.prlist_val[i] != glist[i]) { | |
602 | fprintf(stderr, | |
603 | "membership for %di not correct: was %di but expected %di\n", | |
604 | ui, alist.prlist_val[i], glist[i]); | |
605 | code++; | |
606 | } | |
607 | if (code) | |
608 | exit(21); | |
609 | if (proc == ubik_PR_GetCPS) { | |
610 | if ((alist.prlist_val[i /* =ng */ ] != AUTHUSERID) || | |
611 | (alist.prlist_val[++i] != ANYUSERID) | |
612 | || (alist.prlist_val[++i] != ui)) { | |
613 | fprintf(stderr, "CPS doesn't have extra entries\n"); | |
614 | exit(27); | |
615 | } | |
616 | } | |
617 | if (alist.prlist_val) | |
618 | free(alist.prlist_val); | |
619 | ||
620 | /* User 0 is a member of all groups all of which should also be on | |
621 | * the owner list of the caller or the ownerUser, although there | |
622 | * may also be others. Check this. */ | |
623 | if (u == 0) { | |
624 | prlist callerList; | |
625 | prlist ownerList; | |
626 | prlist lastGroupList; | |
627 | int i, j, k, l; | |
628 | ||
629 | if (ng != number) { | |
630 | fprintf(stderr, "User 0 not a member of all groups\n"); | |
631 | exit(26); | |
632 | } | |
633 | #define GETOWNED(xlist,xid) \ | |
634 | (xlist).prlist_val = 0; (xlist).prlist_len = 0; \ | |
635 | code = ubik_PR_ListOwned(pruclient, 0, (xid), &(xlist), &over); \ | |
636 | if (code) { \ | |
637 | afs_com_err (whoami, code, "getting owner list of (%di)", (xid)); \ | |
638 | exit (23); } \ | |
639 | if (over) \ | |
640 | { fprintf (stderr, "membership of id %di too long\n", (xid)); } | |
641 | ||
642 | GETOWNED(callerList, callerId); | |
643 | GETOWNED(ownerList, ownerUser); | |
644 | ||
645 | /* look for every entry in glist, in all the owner lists */ | |
646 | for (i = j = k = l = 0; i < number; i++) { | |
647 | while ((j < callerList.prlist_len) | |
648 | && (callerList.prlist_val[j] < glist[i])) | |
649 | j++; | |
650 | while ((k < ownerList.prlist_len) | |
651 | && (ownerList.prlist_val[k] < glist[i])) | |
652 | k++; | |
653 | #define PRLISTCMP(l,i) \ | |
654 | (((l).prlist_len == 0) || (glist[i] != (l).prlist_val[(i)])) | |
655 | if (PRLISTCMP(callerList, j) && PRLISTCMP(ownerList, k)) { | |
656 | for (l = 0; l < number; l++) { | |
657 | if (groups[l] == glist[i]) { | |
658 | if ((groupOwners[l] != callerId) | |
659 | && (groupOwners[l] != ownerUser)) { | |
660 | GETOWNED(lastGroupList, groupOwners[l]); | |
661 | if ((lastGroupList.prlist_len != 1) | |
662 | || (lastGroupList.prlist_val[0] != | |
663 | glist[i])) { | |
664 | fprintf(stderr, | |
665 | "Group (%di) not on any owner list\n", | |
666 | glist[i]); | |
667 | exit(25); | |
668 | } | |
669 | } | |
670 | goto foundLast; | |
671 | } | |
672 | } | |
673 | fprintf(stderr, "unexpected group %di\n", glist[i]); | |
674 | foundLast:; | |
675 | } | |
676 | } | |
677 | if (callerList.prlist_val) | |
678 | free(callerList.prlist_val); | |
679 | if (ownerList.prlist_val) | |
680 | free(ownerList.prlist_val); | |
681 | if (lastGroupList.prlist_val) | |
682 | free(lastGroupList.prlist_val); | |
683 | } | |
684 | } | |
685 | } | |
686 | ||
687 | /* cleanup by deleting all the users and groups */ | |
688 | printf("Starting deletion of users and groups\n"); | |
689 | for (i = 0; i < number; i++) { | |
690 | DeleteRandomId(users); | |
691 | DeleteRandomId(groups); | |
692 | } | |
693 | ||
694 | printf | |
695 | ("Created/deleted %d/%d users and %d/%d groups; added %d and removed %d.\n", | |
696 | nUsers, nUDels, nGroups, nGDels, nAdds, nRems); | |
697 | return 0; | |
698 | } | |
699 | ||
700 | /* from ka_ConvertBytes included here to avoid circularity */ | |
701 | /* Converts a byte string to ascii. Return the number of unconverted bytes. */ | |
702 | ||
703 | static int | |
704 | ka_ConvertBytes(char *ascii, /* output buffer */ | |
705 | int alen, /* buffer length */ | |
706 | char bs[], /* byte string */ | |
707 | int bl) /* number of bytes */ | |
708 | { | |
709 | int i; | |
710 | unsigned char c; | |
711 | ||
712 | alen--; /* make room for termination */ | |
713 | for (i = 0; i < bl; i++) { | |
714 | c = bs[i]; | |
715 | if (alen <= 0) | |
716 | return bl - i; | |
717 | if (isalnum(c) || ispunct(c)) | |
718 | (*ascii++ = c), alen--; | |
719 | else { | |
720 | if (alen <= 3) | |
721 | return bl - i; | |
722 | *ascii++ = '\\'; | |
723 | *ascii++ = (c >> 6) + '0'; | |
724 | *ascii++ = (c >> 3 & 7) + '0'; | |
725 | *ascii++ = (c & 7) + '0'; | |
726 | alen -= 4; | |
727 | } | |
728 | } | |
729 | *ascii = 0; /* terminate string */ | |
730 | return 0; | |
731 | } | |
732 | ||
733 | /* This runs various tests on the server. It creates, then deletes, a bunch of | |
734 | * users and groups, so it would be safest to run it on a test database. | |
735 | * | |
736 | * These are the things I check for: | |
737 | * User names longer than PR_MAXNAMELEN - strlen(cellname). | |
738 | * Group names longer than PR_MAXNAMELEN. | |
739 | * User names containing all legal 8-bit ascii characters. This excludes | |
740 | * only ':', '@', and '\n'. | |
741 | * Group names as above, but at least one colon is required, and the owner | |
742 | * must be correct. | |
743 | */ | |
744 | ||
745 | int | |
746 | TestPrServ(struct cmd_syndesc *as, void *arock) | |
747 | { | |
748 | afs_int32 id; | |
749 | char name[PR_MAXNAMELEN + 1]; | |
750 | char creator[PR_MAXNAMELEN]; /* our name */ | |
751 | struct prcheckentry ent; | |
752 | afs_int32 code; | |
753 | int i, j; | |
754 | int maxLen = PR_MAXNAMELEN - 1 - strlen(lcell) - 1; | |
755 | ||
756 | code = pr_Initialize(1, conf_dir, NULL); | |
757 | if (code) { | |
758 | afs_com_err(whoami, code, "initializing pruser"); | |
759 | exit(1); | |
760 | } | |
761 | ||
762 | for (i = 0; i < maxLen; i++) | |
763 | name[i] = 'a'; | |
764 | name[i] = 'a'; /* too long a name... */ | |
765 | name[i + 1] = 0; | |
766 | id = 0; | |
767 | code = pr_CreateUser(name, &id); | |
768 | if ((code != RXGEN_CC_MARSHAL) && (code != PRBADNAM)) { | |
769 | afs_com_err(whoami, code, "succeeded creating %s", name); | |
770 | exit(2); | |
771 | } | |
772 | name[i] = 0; | |
773 | id = 0; | |
774 | code = pr_CreateUser(name, &id); | |
775 | if (code == PREXIST) { | |
776 | fprintf(stderr, "group already exists, skipping\n"); | |
777 | pr_SNameToId(name, &id); | |
778 | } else if (code) { | |
779 | afs_com_err(whoami, code, "failed creating %s", name); | |
780 | exit(3); | |
781 | } | |
782 | if ((code = pr_ListEntry(id, &ent)) | |
783 | || (code = pr_SIdToName(ent.creator, creator))) { | |
784 | afs_com_err(whoami, code, "getting creator's name"); | |
785 | exit(5); | |
786 | } | |
787 | code = pr_DeleteByID(id); | |
788 | if (code) { | |
789 | afs_com_err(whoami, code, "deleting %s", name); | |
790 | exit(6); | |
791 | } | |
792 | /* now make sure the illegal chars are detected */ | |
793 | { | |
794 | char *illegalChars; | |
795 | for (illegalChars = "@:\n"; *illegalChars; illegalChars++) { | |
796 | name[10] = *illegalChars; | |
797 | id = 0; | |
798 | code = pr_CreateUser(name, &id); | |
799 | if (code != PRBADNAM) { | |
800 | afs_com_err(whoami, code, "succeeded creating %s", name); | |
801 | exit(8); | |
802 | } | |
803 | } | |
804 | } | |
805 | ||
806 | for (i = 1; i <= 255;) { /* for all 8-bit ascii... */ | |
807 | j = 0; /* build a new name */ | |
808 | while ((j < maxLen) && (i <= 255)) { | |
809 | if (!((i == ':') || (i == '@') || (i == '\n'))) | |
810 | name[j++] = i; | |
811 | i++; | |
812 | } | |
813 | name[j] = 0; /* terminate string */ | |
814 | id = 0; | |
815 | code = pr_CreateUser(name, &id); | |
816 | if (code == PREXIST) { | |
817 | fprintf(stderr, "user already exists, skipping\n"); | |
818 | pr_SNameToId(name, &id); | |
819 | } else if (code) { | |
820 | char ascii[BUFSIZ]; | |
821 | ka_ConvertBytes(ascii, sizeof(ascii), name, strlen(name)); | |
822 | afs_com_err(whoami, code, "failed creating %s", ascii); | |
823 | exit(4); | |
824 | } | |
825 | code = pr_DeleteByID(id); | |
826 | if (code) { | |
827 | afs_com_err(whoami, code, "deleting %s", name); | |
828 | exit(7); | |
829 | } | |
830 | } | |
831 | ||
832 | /* now check group names */ | |
833 | strcpy(name, creator); | |
834 | strcat(name, ":abcdefghijklmnopqrstuvwxyz"); | |
835 | name[0] = 1; /* bash the owner name */ | |
836 | id = 0; | |
837 | code = pr_CreateGroup(name, creator, &id); | |
838 | if (code != PRNOENT) { /* owner doesn't exist */ | |
839 | afs_com_err(whoami, code, "succeeded creating %s", name); | |
840 | exit(9); | |
841 | } | |
842 | name[0] = creator[0]; /* fix owner */ | |
843 | /* Make sure the illegal chars are detected */ | |
844 | { | |
845 | char *illegalChars; | |
846 | for (illegalChars = ":@\n"; *illegalChars; illegalChars++) { | |
847 | name[strlen(creator) + 10] = *illegalChars; | |
848 | id = 0; | |
849 | code = pr_CreateGroup(name, creator, &id); | |
850 | if (code != PRBADNAM) { | |
851 | afs_com_err(whoami, code, "succeeded creating %s", name); | |
852 | exit(10); | |
853 | } | |
854 | } | |
855 | } | |
856 | for (i = 1; i <= 255;) { /* for all 8-bit ascii... */ | |
857 | j = strlen(creator) + 1; /* build a new name */ | |
858 | while ((j < PR_MAXNAMELEN - 1) && (i <= 255)) { | |
859 | if (!((i == ':') || (i == '@') || (i == '\n'))) | |
860 | name[j++] = i; | |
861 | i++; | |
862 | } | |
863 | name[j] = 0; /* terminate string */ | |
864 | id = 0; | |
865 | code = pr_CreateGroup(name, creator, &id); | |
866 | if (code == PREXIST) { | |
867 | fprintf(stderr, "group already exists, skipping\n"); | |
868 | pr_SNameToId(name, &id); | |
869 | } else if (code) { | |
870 | char ascii[BUFSIZ]; | |
871 | ka_ConvertBytes(ascii, sizeof(ascii), name, strlen(name)); | |
872 | afs_com_err(whoami, code, "failed creating %s", ascii); | |
873 | exit(4); | |
874 | } | |
875 | code = pr_DeleteByID(id); | |
876 | if (code) { | |
877 | afs_com_err(whoami, code, "deleting %s", name); | |
878 | exit(7); | |
879 | } | |
880 | } | |
881 | ||
882 | printf("All OK\n"); | |
883 | return 0; | |
884 | } | |
885 | ||
886 | static char tmp_conf_dir[128] = ""; | |
887 | static char tmp_conf_file[128] = ""; | |
888 | static char tmp_cell_file[128] = ""; | |
889 | static char tmp_noauth_file[128] = ""; | |
890 | ||
891 | static int | |
892 | MyAfterProc(struct cmd_syndesc *as, void *arock) | |
893 | { | |
894 | if (strlen(tmp_conf_file)) | |
895 | unlink(tmp_conf_file); | |
896 | if (strlen(tmp_cell_file)) | |
897 | unlink(tmp_cell_file); | |
898 | if (strlen(tmp_noauth_file)) | |
899 | unlink(tmp_noauth_file); | |
900 | if (strlen(tmp_conf_dir)) | |
901 | rmdir(tmp_conf_dir); | |
902 | return 0; | |
903 | } | |
904 | ||
905 | static int | |
906 | MyBeforeProc(struct cmd_syndesc *as, void *arock) | |
907 | { | |
908 | afs_int32 code; | |
909 | int i; | |
910 | char *cdir = 0; | |
911 | int noAuth = 0; | |
912 | struct cmd_item *serverList = 0; | |
913 | struct afsconf_dir *local_conf = 0; /* so we can default stuff nicely */ | |
914 | struct afsconf_cell cellinfo; | |
915 | ||
916 | if (as->parms[12].items) { /* if conf dir specified */ | |
917 | cdir = as->parms[12].items->data; | |
918 | if (as->parms[13].items || as->parms[14].items || as->parms[15].items) { | |
919 | printf("Can't specify conf dir and other cell parameters\n"); | |
920 | return AFSCONF_SYNTAX; | |
921 | } | |
922 | } | |
923 | ||
924 | /* if we need to default cell name or cell servers, get local conf info */ | |
925 | ||
926 | if (!(local_conf = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH)) | |
927 | && !(local_conf = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH))) { | |
928 | printf("** Can't local configuration!\n"); | |
929 | return AFSCONF_NOCELL; | |
930 | } | |
931 | ||
932 | if (as->parms[13].items) { /* if cell name specified */ | |
933 | lcstring(lcell, as->parms[13].items->data, sizeof(lcell)); | |
934 | code = afsconf_GetCellInfo(local_conf, lcell, 0, &cellinfo); | |
935 | if (code == 0) | |
936 | strncpy(lcell, cellinfo.name, sizeof(lcell)); | |
937 | } else { | |
938 | code = afsconf_GetLocalCell(local_conf, lcell, sizeof(lcell)); | |
939 | if (code) | |
940 | return code; | |
941 | } | |
942 | ||
943 | if (as->parms[14].items) { /* noauth flag */ | |
944 | noAuth = 1; | |
945 | } | |
946 | ||
947 | if (as->parms[15].items) { /* servers list */ | |
948 | serverList = as->parms[15].items; | |
949 | for (i = 0; serverList; i++, serverList = serverList->next) { | |
950 | struct hostent *th; | |
951 | if (i >= MAXHOSTSPERCELL) | |
952 | return AFSCONF_FULL; | |
953 | strncpy(cellinfo.hostName[i], serverList->data, MAXHOSTCHARS); | |
954 | th = gethostbyname(cellinfo.hostName[i]); | |
955 | if (!th) | |
956 | return UBADHOST; | |
957 | memcpy(&cellinfo.hostAddr[i].sin_addr, th->h_addr, | |
958 | sizeof(afs_int32)); | |
959 | cellinfo.hostAddr[i].sin_family = AF_INET; | |
960 | cellinfo.hostAddr[i].sin_port = 0; | |
961 | #ifdef STRUCT_SOCKADDR_HAS_SA_LEN | |
962 | cellinfo.hostAddr[i].sin_len = sizeof(struct sockaddr_in); | |
963 | #endif | |
964 | } | |
965 | cellinfo.numServers = i; | |
966 | strcpy(cellinfo.name, lcell); | |
967 | } else { | |
968 | code = afsconf_GetCellInfo(local_conf, lcell, 0, &cellinfo); | |
969 | if (code) | |
970 | return code; | |
971 | } | |
972 | ||
973 | if (local_conf) | |
974 | afsconf_Close(local_conf); | |
975 | ||
976 | if (cdir == 0) { | |
977 | FILE *f; | |
978 | ||
979 | sprintf(tmp_conf_dir, "%s/afsconf.%lu", gettmpdir(), | |
980 | (unsigned long)getpid()); | |
981 | code = mkdir(tmp_conf_dir, 0777); | |
982 | if ((code < 0) && (errno != EEXIST)) { | |
983 | afs_com_err(whoami, errno, "can't create temporary afsconf dir: %s", | |
984 | cdir); | |
985 | return errno; | |
986 | } | |
987 | ||
988 | strcompose(tmp_conf_file, 128, tmp_conf_dir, "/", | |
989 | AFSDIR_CELLSERVDB_FILE, (char *)NULL); | |
990 | f = fopen(tmp_conf_file, "w"); | |
991 | if (f == 0) { | |
992 | cantcreate: | |
993 | afs_com_err(whoami, errno, "can't create conf file %s", | |
994 | tmp_conf_file); | |
995 | return errno; | |
996 | } | |
997 | fprintf(f, ">%s\n", lcell); | |
998 | for (i = 0; i < cellinfo.numServers; i++) { | |
999 | unsigned char *tp = | |
1000 | (unsigned char *)&cellinfo.hostAddr[i].sin_addr; | |
1001 | fprintf(f, "%d.%d.%d.%d\t#%s\n", tp[0], tp[1], tp[2], tp[3], | |
1002 | cellinfo.hostName[i]); | |
1003 | } | |
1004 | if (fclose(f) == EOF) { | |
1005 | cantclose: | |
1006 | afs_com_err(whoami, errno, "can't write to conf file %s", | |
1007 | tmp_conf_file); | |
1008 | return errno; | |
1009 | } | |
1010 | ||
1011 | strcompose(tmp_cell_file, 128, tmp_conf_dir, "/", | |
1012 | AFSDIR_THISCELL_FILE, (char *)NULL); | |
1013 | f = fopen(tmp_cell_file, "w"); | |
1014 | if (f == 0) | |
1015 | goto cantcreate; | |
1016 | fprintf(f, "%s", lcell); | |
1017 | if (fclose(f) == EOF) | |
1018 | goto cantclose; | |
1019 | ||
1020 | strcompose(tmp_noauth_file, 128, tmp_conf_dir, "/", | |
1021 | AFSDIR_NOAUTH_FILE, (char *)NULL); | |
1022 | if (noAuth) { | |
1023 | code = creat(tmp_noauth_file, 0777); | |
1024 | if (code && (errno != EEXIST)) | |
1025 | return errno; | |
1026 | } else { /* make sure file doesn't exist */ | |
1027 | code = unlink(tmp_noauth_file); | |
1028 | if (code && (errno != ENOENT)) | |
1029 | return errno; | |
1030 | } | |
1031 | } | |
1032 | ||
1033 | strncpy(conf_dir, tmp_conf_dir, sizeof(conf_dir)); | |
1034 | conf = afsconf_Open(conf_dir); | |
1035 | if (conf == 0) | |
1036 | return AFSCONF_NOTFOUND; | |
1037 | return 0; | |
1038 | } | |
1039 | ||
1040 | static void | |
1041 | add_std_args(struct cmd_syndesc *ts) | |
1042 | { | |
1043 | cmd_Seek(ts, 12); | |
1044 | cmd_AddParm(ts, "-confdir", CMD_SINGLE, CMD_OPTIONAL, | |
1045 | "AFS Conf dir pathname"); | |
1046 | cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "Cell name"); | |
1047 | cmd_AddParm(ts, "-noauth", CMD_FLAG, CMD_OPTIONAL, "Don't authenticate"); | |
1048 | cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL, "Server config"); | |
1049 | } | |
1050 | ||
1051 | int | |
1052 | osi_audit(void) | |
1053 | { | |
1054 | /* OK, this REALLY sucks bigtime, but I can't tell who is calling | |
1055 | * afsconf_CheckAuth easily, and only *SERVERS* should be calling osi_audit | |
1056 | * anyway. It's gonna give somebody fits to debug, I know, I know. | |
1057 | */ | |
1058 | return 0; | |
1059 | } | |
1060 | ||
1061 | #include "AFS_component_version_number.c" | |
1062 | ||
1063 | int | |
1064 | main(int argc, char *argv[]) | |
1065 | { | |
1066 | afs_int32 code; | |
1067 | struct cmd_syndesc *ts; /* ptr to parsed command line syntax */ | |
1068 | ||
1069 | whoami = argv[0]; | |
1070 | initialize_CMD_error_table(); | |
1071 | initialize_ACFG_error_table(); | |
1072 | initialize_KTC_error_table(); | |
1073 | initialize_U_error_table(); | |
1074 | initialize_PT_error_table(); | |
1075 | initialize_RXK_error_table(); | |
1076 | ||
1077 | #ifdef AFS_NT40_ENV | |
1078 | /* initialize winsock */ | |
1079 | if (afs_winsockInit() < 0) { | |
1080 | fprintf(stderr, "%s: couldn't initialize winsock. \n", whoami); | |
1081 | exit(1); | |
1082 | } | |
1083 | #endif | |
1084 | ||
1085 | cmd_SetBeforeProc(MyBeforeProc, NULL); | |
1086 | cmd_SetAfterProc(MyAfterProc, NULL); | |
1087 | ||
1088 | ts = cmd_CreateSyntax("usedIds", ListUsedIds, NULL, 0, | |
1089 | "Find used (or unused) user (or group) ids"); | |
1090 | cmd_AddParm(ts, "-startId", CMD_SINGLE, CMD_OPTIONAL, | |
1091 | "id to start checking"); | |
1092 | cmd_AddParm(ts, "-number", CMD_SINGLE, CMD_OPTIONAL, | |
1093 | "number of ids to check"); | |
1094 | cmd_AddParm(ts, "-unused", CMD_FLAG, CMD_OPTIONAL, "print unused ids"); | |
1095 | add_std_args(ts); | |
1096 | ||
1097 | ts = cmd_CreateSyntax("initcmd", TestPrServ, NULL, 0, "test the prserver"); | |
1098 | add_std_args(ts); | |
1099 | ||
1100 | ts = cmd_CreateSyntax("testmanymembers", TestManyMembers, NULL, 0, | |
1101 | "test creating users and groups w/ many members"); | |
1102 | cmd_AddParm(ts, "-number", CMD_SINGLE, 0, | |
1103 | "number of users/groups to create"); | |
1104 | cmd_AddParm(ts, "-dropoff", CMD_SINGLE, CMD_OPTIONAL, | |
1105 | "precentage for exponential dropoff"); | |
1106 | cmd_AddParm(ts, "-prefix", CMD_SINGLE, CMD_OPTIONAL, "naming prefix"); | |
1107 | cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL, "show progress"); | |
1108 | cmd_AddParm(ts, "-seed", CMD_SINGLE, CMD_OPTIONAL, "random number seed"); | |
1109 | add_std_args(ts); | |
1110 | cmd_CreateAlias(ts, "mm"); | |
1111 | ||
1112 | ||
1113 | code = cmd_Dispatch(argc, argv); | |
1114 | if (code) | |
1115 | afs_com_err(whoami, code, "calling cmd_Dispatch"); | |
1116 | exit(code); | |
1117 | } |