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