]> git.saurik.com Git - apple/security.git/blob - keychain/ckksctl/ckksctl.m
Security-58286.51.6.tar.gz
[apple/security.git] / keychain / ckksctl / ckksctl.m
1 //
2 // Security
3 //
4
5 #import <Foundation/Foundation.h>
6 #import <Foundation/NSXPCConnection_Private.h>
7 #import <Security/Security.h>
8 #import <Security/SecItemPriv.h>
9 #import <xpc/xpc.h>
10 #import <err.h>
11
12 #import "keychain/ckks/CKKS.h"
13 #import "keychain/ckks/CKKSControl.h"
14
15 #include "lib/SecArgParse.h"
16
17 static void nsprintf(NSString *fmt, ...) NS_FORMAT_FUNCTION(1, 2);
18 static void print_result(NSDictionary *dict, bool json_flag);
19 static void print_dict(NSDictionary *dict, int ind);
20 static void print_array(NSArray *array, int ind);
21 static void print_entry(id k, id v, int ind);
22
23 static void nsprintf(NSString *fmt, ...)
24 {
25 va_list ap;
26 va_start(ap, fmt);
27 NSString *str = [[NSString alloc] initWithFormat:fmt arguments:ap];
28 va_end(ap);
29
30 puts([str UTF8String]);
31 #if !__has_feature(objc_arc)
32 [str release];
33 #endif
34 }
35
36 static NSDictionary* flattenNSErrorsInDictionary(NSDictionary* dict) {
37 if(!dict) {
38 return nil;
39 }
40 NSMutableDictionary* mutDict = [dict mutableCopy];
41 for(id key in mutDict.allKeys) {
42 id obj = mutDict[key];
43 if([obj isKindOfClass:[NSError class]]) {
44 NSError* obje = (NSError*) obj;
45 NSMutableDictionary* newErrorDict = [@{@"code": @(obje.code), @"domain": obje.domain} mutableCopy];
46 newErrorDict[@"userInfo"] = flattenNSErrorsInDictionary(obje.userInfo);
47 mutDict[key] = newErrorDict;
48 } else if(![NSJSONSerialization isValidJSONObject:obj]) {
49 mutDict[key] = [obj description];
50 }
51 }
52 return mutDict;
53 }
54
55 static void print_result(NSDictionary *dict, bool json_flag)
56 {
57 if (json_flag) {
58 NSError *err;
59
60 // NSErrors don't know how to JSON-ify themselves, for some reason
61 // This will flatten a single layer of them
62 if(![NSJSONSerialization isValidJSONObject:dict]) {
63 dict = flattenNSErrorsInDictionary(dict);
64 }
65
66 if(![NSJSONSerialization isValidJSONObject:dict]) {
67 printf("Still unsure how to JSONify the following object:\n");
68 print_dict(dict, 0);
69 }
70
71 NSData *json = [NSJSONSerialization dataWithJSONObject:dict
72 options:(NSJSONWritingPrettyPrinted | NSJSONWritingSortedKeys)
73 error:&err];
74 if (!json) {
75 NSLog(@"error: %@", err.localizedDescription);
76 } else {
77 printf("%s", [[[NSString alloc] initWithData:json encoding:NSUTF8StringEncoding] UTF8String]);
78 }
79 } else {
80 print_dict(dict, 0);
81 }
82 }
83
84 static void print_dict(NSDictionary *dict, int ind)
85 {
86 NSArray *sortedKeys = [[dict allKeys] sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
87 for (id k in sortedKeys) {
88 id v = dict[k];
89 print_entry(k, v, ind);
90 }
91 }
92
93 static void print_array(NSArray *array, int ind)
94 {
95 [array enumerateObjectsUsingBlock:^(id v, NSUInteger i, BOOL *stop __unused) {
96 print_entry(@(i), v, ind);
97 }];
98 }
99
100 static void print_entry(id k, id v, int ind)
101 {
102 if ([v isKindOfClass:[NSDictionary class]]) {
103 if (ind == 0) {
104 nsprintf(@"\n%*s%@ -", ind * 4, "", k);
105 nsprintf(@"%*s========================", ind * 4, "");
106 } else if (ind == 1) {
107 nsprintf(@"\n%*s%@ -", ind * 4, "", k);
108 nsprintf(@"%*s------------------------", ind * 4, "");
109 } else {
110 nsprintf(@"%*s%@ -", ind * 4, "", k);
111 }
112
113 print_dict(v, ind + 1);
114 } else if ([v isKindOfClass:[NSArray class]]) {
115 nsprintf(@"%*s%@ -", ind * 4, "", k);
116 print_array(v, ind + 1);
117 } else {
118 nsprintf(@"%*s%@: %@", ind * 4, "", k, v);
119 }
120 }
121
122 @interface CKKSControlCLI : NSObject
123 @property CKKSControl* control;
124 @end
125
126 @implementation CKKSControlCLI
127
128 - (instancetype) initWithCKKSControl:(CKKSControl*)control {
129 if ((self = [super init])) {
130 _control = control;
131 }
132
133 return self;
134 }
135
136 - (NSDictionary<NSString *, id> *)fetchPerformanceCounters
137 {
138 NSMutableDictionary *perfDict = [[NSMutableDictionary alloc] init];
139 #if OCTAGON
140 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
141
142 [self.control rpcPerformanceCounters:^(NSDictionary<NSString *,NSNumber *> * counters, NSError * error) {
143 if(error) {
144 perfDict[@"error"] = [error description];
145 }
146
147 [counters enumerateKeysAndObjectsUsingBlock:^(NSString * key, NSNumber * obj, BOOL *stop) {
148 perfDict[key] = obj;
149 }];
150
151 dispatch_semaphore_signal(sema);
152 }];
153
154 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60)) != 0) {
155 perfDict[@"error"] = @"timed out waiting for response";
156 }
157 #endif
158
159 return perfDict;
160 }
161
162 - (long)resetLocal:(NSString*)view {
163 __block long ret = 0;
164 #if OCTAGON
165 printf("Beginning local reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
166 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
167
168 [self.control rpcResetLocal:view
169 reply:^(NSError *error) {
170 if(error == NULL) {
171 printf("reset complete.\n");
172 ret = 0;
173 } else {
174 nsprintf(@"reset error: %@\n", error);
175 ret = error.code;
176 }
177 dispatch_semaphore_signal(sema);
178 }];
179
180 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60 * 2)) != 0) {
181 printf("\n\nError: timed out waiting for response\n");
182 return -1;
183 }
184 #endif // OCTAGON
185 return ret;
186 }
187
188 - (long)resetCloudKit:(NSString*)view {
189 __block long ret = 0;
190 #if OCTAGON
191 printf("Beginning CloudKit reset for %s...\n", view ? [[view description] UTF8String] : "all zones");
192 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
193
194 [self.control rpcResetCloudKit:view reply:^(NSError* error){
195 if(error == NULL) {
196 printf("CloudKit Reset complete.\n");
197 ret = 0;
198 } else {
199 nsprintf(@"Reset error: %@\n", error);
200 ret = error.code;
201 }
202 dispatch_semaphore_signal(sema);
203 }];
204
205 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60 * 5)) != 0) {
206 printf("\n\nError: timed out waiting for response\n");
207 return -1;
208 }
209 #endif // OCTAON
210 return ret;
211 }
212
213 - (long)resync:(NSString*)view {
214 __block long ret = 0;
215 #if OCTAGON
216 printf("Beginning resync for %s...\n", view ? [[view description] UTF8String] : "all zones");
217 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
218
219 [self.control rpcResync:view reply:^(NSError* error){
220 if(error == NULL) {
221 printf("resync success.\n");
222 ret = 0;
223 } else {
224 nsprintf(@"resync errored: %@\n", error);
225 ret = error.code;
226 }
227 dispatch_semaphore_signal(sema);
228 }];
229
230 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 60 * 2)) != 0) {
231 printf("\n\nError: timed out waiting for response\n");
232 return -1;
233 }
234 #endif // OCTAGON
235 return ret;
236 }
237
238 - (NSDictionary<NSString *, id> *)fetchStatus: (NSString*) view {
239 NSMutableDictionary *status = [[NSMutableDictionary alloc] init];
240 #if OCTAGON
241 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
242
243 [self.control rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
244 if(error) {
245 status[@"error"] = [error description];
246 }
247
248 if(result.count <= 1u) {
249 printf("No CKKS views are active.\n");
250 }
251
252
253 for(NSDictionary* viewStatus in result) {
254 status[viewStatus[@"view"]] = viewStatus;
255 }
256 dispatch_semaphore_signal(sema);
257 }];
258
259 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
260 status[@"error"] = @"timed out";
261 }
262 #endif // OCTAGON
263 return status;
264 }
265
266 - (void)printHumanReadableStatus: (NSString*) view {
267 #if OCTAGON
268 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
269
270 [self.control rpcStatus: view reply: ^(NSArray<NSDictionary*>* result, NSError* error) {
271 if(error) {
272 printf("ERROR FETCHING STATUS: %s\n", [[error description] UTF8String]);
273 }
274
275 #define pop(d, key) ({ id x = d[key]; d[key] = nil; x; })
276
277 // First result is always global state
278 // Ideally, this would come in another parameter, but we can't change the protocol until
279 // <rdar://problem/33583242> CKKS: remove PCS's use of CKKSControlProtocol
280 NSMutableDictionary* global = [result[0] mutableCopy];
281 if(global) {
282 NSString* selfPeers = pop(global, @"selfPeers");
283 NSString* selfPeersError = pop(global, @"selfPeersError");
284 NSArray* trustedPeers = pop(global, @"trustedPeers");
285 NSString* trustedPeersError = pop(global, @"trustedPeersError");
286 NSString* reachability = pop(global, @"reachability");
287 NSString* ckdeviceID = pop(global, @"ckdeviceID");
288 NSString* ckdeviceIDError = pop(global, @"ckdeviceIDError");
289
290 printf("================================================================================\n\n");
291 printf("Global state:\n\n");
292 printf("Current self: %s\n", [[selfPeers description] UTF8String]);
293 if(![selfPeersError isEqual: [NSNull null]]) {
294 printf("Self Peers Error: %s\n", [[selfPeersError description] UTF8String]);
295 }
296 printf("Trusted peers: %s\n", [[trustedPeers description] UTF8String]);
297 if(![trustedPeersError isEqual: [NSNull null]]) {
298 printf("Trusted Peers Error: %s\n", [[trustedPeersError description] UTF8String]);
299 }
300 printf("Reachability: %s\n", [[reachability description] UTF8String]);
301 printf("CK DeviceID: %s\n", [[ckdeviceID description] UTF8String]);
302 printf("CK DeviceID Error: %s\n", [[ckdeviceIDError description] UTF8String]);
303
304 printf("\n");
305 }
306
307 NSArray* remainingViews = result.count > 1 ? [result subarrayWithRange:NSMakeRange(1, result.count-1)] : @[];
308
309 if(remainingViews.count == 0u) {
310 printf("No CKKS views are active.\n");
311 }
312
313 for(NSDictionary* viewStatus in remainingViews) {
314 NSMutableDictionary* status = [viewStatus mutableCopy];
315
316 NSString* viewName = pop(status,@"view");
317 NSString* accountStatus = pop(status,@"ckaccountstatus");
318 NSString* lockStateTracker = pop(status,@"lockstatetracker");
319 NSString* accountTracker = pop(status,@"accounttracker");
320 NSString* fetcher = pop(status,@"fetcher");
321 NSString* zoneCreated = pop(status,@"zoneCreated");
322 NSString* zoneCreatedError = pop(status,@"zoneCreatedError");
323 NSString* zoneSubscribed = pop(status,@"zoneSubscribed");
324 NSString* zoneSubscribedError = pop(status,@"zoneSubscribedError");
325 NSString* zoneInitializeScheduler = pop(status,@"zoneInitializeScheduler");
326 NSString* keystate = pop(status,@"keystate");
327 NSString* keyStateError = pop(status,@"keyStateError");
328 NSString* statusError = pop(status,@"statusError");
329 NSString* currentTLK = pop(status,@"currentTLK");
330 NSString* currentClassA = pop(status,@"currentClassA");
331 NSString* currentClassC = pop(status,@"currentClassC");
332 NSString* currentTLKPtr = pop(status,@"currentTLKPtr");
333 NSString* currentClassAPtr = pop(status,@"currentClassAPtr");
334 NSString* currentClassCPtr = pop(status,@"currentClassCPtr");
335 NSString* currentManifestGeneration = pop(status,@"currentManifestGen");
336
337 NSDictionary* oqe = pop(status,@"oqe");
338 NSDictionary* iqe = pop(status,@"iqe");
339 NSDictionary* keys = pop(status,@"keys");
340 NSDictionary* ckmirror = pop(status,@"ckmirror");
341 NSArray* devicestates = pop(status, @"devicestates");
342 NSArray* tlkshares = pop(status, @"tlkshares");
343
344
345 NSString* zoneSetupOperation = pop(status,@"zoneSetupOperation");
346 NSString* keyStateOperation = pop(status,@"keyStateOperation");
347 NSString* lastIncomingQueueOperation = pop(status,@"lastIncomingQueueOperation");
348 NSString* lastNewTLKOperation = pop(status,@"lastNewTLKOperation");
349 NSString* lastOutgoingQueueOperation = pop(status,@"lastOutgoingQueueOperation");
350 NSString* lastRecordZoneChangesOperation = pop(status,@"lastRecordZoneChangesOperation");
351 NSString* lastProcessReceivedKeysOperation = pop(status,@"lastProcessReceivedKeysOperation");
352 NSString* lastReencryptOutgoingItemsOperation = pop(status,@"lastReencryptOutgoingItemsOperation");
353 NSString* lastScanLocalItemsOperation = pop(status,@"lastScanLocalItemsOperation");
354
355 printf("================================================================================\n\n");
356
357 printf("View: %s\n\n", [viewName UTF8String]);
358
359 if(![statusError isEqual: [NSNull null]]) {
360 printf("ERROR FETCHING STATUS: %s\n\n", [statusError UTF8String]);
361 }
362
363 printf("CloudKit account: %s\n", [accountStatus UTF8String]);
364 printf("Account tracker: %s\n", [accountTracker UTF8String]);
365
366 if(!([zoneCreated isEqualToString:@"yes"] && [zoneSubscribed isEqualToString:@"yes"])) {
367 printf("CK Zone Created: %s\n", [[zoneCreated description] UTF8String]);
368 printf("CK Zone Created error: %s\n", [[zoneCreatedError description] UTF8String]);
369
370 printf("CK Zone Subscribed: %s\n", [[zoneSubscribed description] UTF8String]);
371 printf("CK Zone Subscription error: %s\n", [[zoneSubscribedError description] UTF8String]);
372 printf("CK Zone initialize retry: %s\n", [[zoneInitializeScheduler description] UTF8String]);
373 printf("\n");
374 }
375
376 printf("Key state: %s\n", [keystate UTF8String]);
377 if(![keyStateError isEqual: [NSNull null]]) {
378 printf("Key State Error: %s\n", [keyStateError UTF8String]);
379 }
380 printf("Lock state: %s\n", [lockStateTracker UTF8String]);
381
382 printf("Current TLK: %s\n", ![currentTLK isEqual: [NSNull null]]
383 ? [currentTLK UTF8String]
384 : [[NSString stringWithFormat:@"missing; pointer is %@", currentTLKPtr] UTF8String]);
385 printf("Current ClassA: %s\n", ![currentClassA isEqual: [NSNull null]]
386 ? [currentClassA UTF8String]
387 : [[NSString stringWithFormat:@"missing; pointer is %@", currentClassAPtr] UTF8String]);
388 printf("Current ClassC: %s\n", ![currentClassC isEqual: [NSNull null]]
389 ? [currentClassC UTF8String]
390 : [[NSString stringWithFormat:@"missing; pointer is %@", currentClassCPtr] UTF8String]);
391
392 printf("TLK shares: %s\n", [[tlkshares description] UTF8String]);
393
394 printf("Outgoing Queue counts: %s\n", [[oqe description] UTF8String]);
395 printf("Incoming Queue counts: %s\n", [[iqe description] UTF8String]);
396 printf("Key counts: %s\n", [[keys description] UTF8String]);
397 printf("latest manifest generation: %s\n", [currentManifestGeneration isEqual:[NSNull null]] ? "null" : currentManifestGeneration.UTF8String);
398
399 printf("Item counts (by key): %s\n", [[ckmirror description] UTF8String]);
400 printf("Peer states: %s\n", [[devicestates description] UTF8String]);
401
402 printf("zone change fetcher: %s\n", [[fetcher description] UTF8String]);
403 printf("zoneSetupOperation: %s\n", [zoneSetupOperation isEqual: [NSNull null]] ? "never" : [zoneSetupOperation UTF8String]);
404 printf("keyStateOperation: %s\n", [keyStateOperation isEqual: [NSNull null]] ? "never" : [keyStateOperation UTF8String]);
405 printf("lastIncomingQueueOperation: %s\n", [lastIncomingQueueOperation isEqual: [NSNull null]] ? "never" : [lastIncomingQueueOperation UTF8String]);
406 printf("lastNewTLKOperation: %s\n", [lastNewTLKOperation isEqual: [NSNull null]] ? "never" : [lastNewTLKOperation UTF8String]);
407 printf("lastOutgoingQueueOperation: %s\n", [lastOutgoingQueueOperation isEqual: [NSNull null]] ? "never" : [lastOutgoingQueueOperation UTF8String]);
408 printf("lastRecordZoneChangesOperation: %s\n", [lastRecordZoneChangesOperation isEqual: [NSNull null]] ? "never" : [lastRecordZoneChangesOperation UTF8String]);
409 printf("lastProcessReceivedKeysOperation: %s\n", [lastProcessReceivedKeysOperation isEqual: [NSNull null]] ? "never" : [lastProcessReceivedKeysOperation UTF8String]);
410 printf("lastReencryptOutgoingItemsOperation: %s\n", [lastReencryptOutgoingItemsOperation isEqual: [NSNull null]] ? "never" : [lastReencryptOutgoingItemsOperation UTF8String]);
411 printf("lastScanLocalItemsOperation: %s\n", [lastScanLocalItemsOperation isEqual: [NSNull null]] ? "never" : [lastScanLocalItemsOperation UTF8String]);
412
413 if(status.allKeys.count > 0u) {
414 printf("\nExtra information: %s\n", [[status description] UTF8String]);
415 }
416 printf("\n");
417 }
418 dispatch_semaphore_signal(sema);
419 }];
420
421 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 30)) != 0) {
422 printf("\n\nError: timed out waiting for response\n");
423 }
424 #endif // OCTAGON
425 }
426
427 - (long)fetch:(NSString*)view {
428 __block long ret = 0;
429 #if OCTAGON
430 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
431
432 [self.control rpcFetchAndProcessChanges:view reply:^(NSError* error) {
433 if(error) {
434 printf("Error fetching: %s\n", [[error description] UTF8String]);
435 ret = (error.code == 0 ? -1 : error.code);
436 } else {
437 printf("Complete.\n");
438 ret = 0;
439 }
440
441 dispatch_semaphore_signal(sema);
442 }];
443
444 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
445 printf("\n\nError: timed out waiting for response\n");
446 return -1;
447 }
448 #endif // OCTAGON
449 return ret;
450 }
451
452 - (long)push:(NSString*)view {
453 __block long ret = 0;
454 #if OCTAGON
455 dispatch_semaphore_t sema = dispatch_semaphore_create(0);
456
457 [self.control rpcPushOutgoingChanges:view reply:^(NSError* error) {
458 if(error) {
459 printf("Error pushing: %s\n", [[error description] UTF8String]);
460 ret = (error.code == 0 ? -1 : error.code);
461 } else {
462 printf("Complete.\n");
463 ret = 0;
464 }
465
466 dispatch_semaphore_signal(sema);
467 }];
468
469 if(dispatch_semaphore_wait(sema, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 65)) != 0) {
470 printf("\n\nError: timed out waiting for response\n");
471 return -1;
472 }
473 #endif // OCTAGON
474 return ret;
475 }
476
477 @end
478
479
480 static int perfCounters = false;
481 static int status = false;
482 static int resync = false;
483 static int reset = false;
484 static int resetCloudKit = false;
485 static int fetch = false;
486 static int push = false;
487 static int json = false;
488
489 static char* viewArg = NULL;
490
491 int main(int argc, char **argv)
492 {
493 static struct argument options[] = {
494 { .shortname='p', .longname="perfcounters", .flag=&perfCounters, .flagval=true, .description="Print CKKS performance counters"},
495 { .shortname='j', .longname="json", .flag=&json, .flagval=true, .description="Output in JSON format"},
496 { .shortname='v', .longname="view", .argument=&viewArg, .description="Operate on a single view"},
497
498 { .command="status", .flag=&status, .flagval=true, .description="Report status on CKKS views"},
499 { .command="fetch", .flag=&fetch, .flagval=true, .description="Fetch all new changes in CloudKit and attempt to process them"},
500 { .command="push", .flag=&push, .flagval=true, .description="Push all pending local changes to CloudKit"},
501 { .command="resync", .flag=&resync, .flagval=true, .description="Resync all data with what's in CloudKit"},
502 { .command="reset", .flag=&reset, .flagval=true, .description="All local data will be wiped, and data refetched from CloudKit"},
503 { .command="reset-cloudkit", .flag=&resetCloudKit, .flagval=true, .description="All data in CloudKit will be removed and replaced with what's local"},
504 {}
505 };
506
507 static struct arguments args = {
508 .programname="ckksctl",
509 .description="Control and report on CKKS",
510 .arguments = options,
511 };
512
513 if(!options_parse(argc, argv, &args)) {
514 printf("\n");
515 print_usage(&args);
516 return -1;
517 }
518
519 @autoreleasepool {
520 NSError* error = nil;
521
522 CKKSControl* rpc = [CKKSControl controlObject:&error];
523 if(error || !rpc) {
524 errx(1, "no CKKSControl failed: %s", [[error description] UTF8String]);
525 }
526
527 CKKSControlCLI* ctl = [[CKKSControlCLI alloc] initWithCKKSControl:rpc];
528
529 NSString* view = viewArg ? [NSString stringWithCString: viewArg encoding: NSUTF8StringEncoding] : nil;
530
531 if(status) {
532 // Complicated logic, but you can choose any combination of (json, perfcounters) that you like.
533 NSMutableDictionary *statusDict = [[NSMutableDictionary alloc] init];
534 if(perfCounters) {
535 statusDict[@"performance"] = [ctl fetchPerformanceCounters];
536 }
537 if (json) {
538 statusDict[@"status"] = [ctl fetchStatus:view];
539 }
540 if(json || perfCounters) {
541 print_result(statusDict, true);
542 printf("\n");
543 }
544
545 if(!json) {
546 [ctl printHumanReadableStatus:view];
547 }
548 return 0;
549 } else if(perfCounters) {
550 NSMutableDictionary *statusDict = [[NSMutableDictionary alloc] init];
551 statusDict[@"performance"] = [ctl fetchPerformanceCounters];
552 print_result(statusDict, false);
553
554 } else if(fetch) {
555 return (int)[ctl fetch:view];
556 } else if(push) {
557 return (int)[ctl push:view];
558 } else if(reset) {
559 return (int)[ctl resetLocal:view];
560 } else if(resetCloudKit) {
561 return (int)[ctl resetCloudKit:view];
562 } else if(resync) {
563 return (int)[ctl resync:view];
564 } else {
565 print_usage(&args);
566 return -1;
567 }
568 }
569 return 0;
570 }