2 // manifeststresstest.m
5 // Created by Ben Williamson on 6/1/17.
9 #import <Foundation/Foundation.h>
16 #import <Security/SecItemPriv.h>
18 static const char *usage_message =
19 "Usage: mainfeststresstest <command> [options...]\n"
23 " reset Delete all items in the access group. Do this before starting the test.\n"
25 " monkey Randomly add, update and delete items for a while. Then delete those items.\n"
26 " --seed <n> Seed the random number generator\n"
27 " --steps <n> Stop after n random actions. Default 1000.\n"
28 " --maxitems <n> Limit number of items created to n. Default 20.\n"
29 " --nocleanup Leave the items in place when finished.\n"
30 " --dryrun Print actions to stdout but don't actually touch the keychain.\n"
31 " --name Specialize items names (to isolate and avoid interfering changes)\n"
32 " --view Keychain syncing view name. If not, specified \"Engram\" is used\n"
34 " mark <id> [view] Write some items containing the string <id>, to mark the known finishing\n"
35 " state for this device. The view argument takes a keychain syncing view name;\n"
36 " If not specified, the view is \"Engram\".\n"
38 " unmark <id> Delete the items that make up the mark for this id.\n"
40 " update <id> Update the items that make up the mark for this id.\n"
42 " verify <id>... Check that the access group contains only the marks for the\n"
43 " given <id> list, corresponding to all the devices being finished.\n"
44 " If given an empty id list this checks the access group is empty.\n"
45 " Exits with nonzero status if verification fails.\n"
47 " verify_update <id>... Check that the access group contains only the updated marks for\n"
48 " the given <id> list, corresponding to all the devices being\n"
49 " finished. If given an empty id list this checks the access group\n"
50 " is empty. Exits with nonzero status if verification fails.\n"
54 " manifeststresstest reset\n"
55 " manifeststresstest monkey --seed 12345 --steps 1000 --maxitems 20\n"
56 " manifeststresstest mark foo\n"
57 " manifeststresstest verify foo bar baz\n"
59 " One device should run reset to clear the contents of the access group before the test\n"
60 " begins. Then all devices should run monkey for a while. When each device is finished\n"
61 " monkeying it should set a pattern with an id that uniquely identifies that device.\n"
62 " When all devices are finished, one device can verify all the patterns by running the\n"
63 " verify command with the ids of all of the devices, as it expects to see the patterns\n"
64 " written by all devices, and no other items.\n"
68 static void usage_exit(void)
70 printf("%s", usage_message);
74 int main(int argc, const char ** argv)
77 Keychain *keychain = [[Keychain alloc] init];
79 NSArray<NSString *> *args = [[NSProcessInfo processInfo] arguments];
80 if ([args count] < 2) {
83 NSString *verb = args[1];
85 if ([verb isEqualToString:@"reset"]) {
87 NSLog(@"reset - deleteAllItems");
88 [keychain deleteAllItems];
90 } else if ([verb isEqualToString:@"monkey"]) {
93 unsigned steps = 1000;
94 Config *config = [[Config alloc] init];
96 config.distinctNames = 40;
97 config.distinctValues = 10;
98 config.addItemWeight = 20;
99 config.deleteItemWeight = 10;
100 config.updateNameWeight = 10;
101 config.updateDataWeight = 10;
102 config.updateNameAndDataWeight = 10;
103 config.view = (__bridge NSString *)kSecAttrViewHintEngram;
106 while (i < [args count]) {
107 NSString *opt = args[i++];
108 if ([opt isEqualToString:@"--seed"]) {
109 if (i >= [args count]) {
110 printf("error: --seed needs a value\n");
113 unsigned seed = (unsigned)[args[i++] integerValue];
114 NSLog(@"Seeding with %d", seed);
116 } else if ([opt isEqualToString:@"--steps"]) {
117 if (i >= [args count]) {
118 printf("error: --steps needs a value\n");
121 steps = (unsigned)[args[i++] integerValue];
122 } else if ([opt isEqualToString:@"--maxitems"]) {
123 if (i >= [args count]) {
124 printf("error: --maxitems needs a value\n");
127 config.maxItems = (unsigned)[args[i++] integerValue];
128 } else if ([opt isEqualToString:@"--nocleanup"]) {
130 } else if ([opt isEqualToString:@"--dryrun"]) {
132 } else if ([opt isEqualToString:@"--name"]) {
133 if (i >= [args count]) {
134 printf("error: --name needs a value\n");
137 config.name = args[i++];
138 } else if ([opt isEqualToString:@"--view"]) {
139 if (i >= [args count]) {
140 printf("error: --view needs a value\n");
143 config.view = args[i++];
145 printf("Unrecognised argument %s\n", [opt UTF8String]);
149 NSLog(@"steps: %d", steps);
150 NSLog(@"maxitems: %d", config.maxItems);
151 NSLog(@"cleanup: %s", cleanup ? "yes" : "no");
152 NSLog(@"dryrun: %s", dryrun ? "yes" : "no");
154 Monkey *monkey = [[Monkey alloc] initWithConfig:config];
156 monkey.keychain = keychain;
159 while (monkey.step < steps) {
160 [monkey advanceOneStep];
167 } else if ([verb isEqualToString:@"mark"]) {
168 if ([args count] < 3) {
169 printf("mark command needs an identifier\n");
172 NSString *ident = args[2];
173 NSString *view = (__bridge NSString*)kSecAttrViewHintEngram;
174 if ([args count] == 4) {
177 NSLog(@"Writing mark %@", ident);
178 writeMark(ident, view);
180 } else if ([verb isEqualToString:@"unmark"]) {
181 if ([args count] < 3) {
182 printf("unmark command needs an identifier\n");
185 NSString *ident = args[2];
186 NSLog(@"Deleting mark %@", ident);
189 } else if ([verb isEqualToString:@"verify"]) {
192 range.length = args.count - 2;
193 NSArray<NSString*> *idents = [args subarrayWithRange:range];
194 NSLog(@"Verifying, idents = %@", idents);
195 if (!verifyMarks(idents)) {
196 NSLog(@"Exiting nonzero status");
200 } else if ([verb isEqualToString:@"update"]) {
201 if ([args count] < 3) {
202 printf("unmark command needs an identifier\n");
205 NSString *ident = args[2];
206 NSLog(@"Updating mark %@", ident);
209 } else if ([verb isEqualToString:@"verify_update"]) {
212 range.length = args.count - 2;
213 NSArray<NSString*> *idents = [args subarrayWithRange:range];
214 NSLog(@"Verifying, idents = %@", idents);
215 if (!verifyUpdateMarks(idents)) {
216 NSLog(@"Exiting nonzero status");