main.m 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  1. //
  2. // main.m
  3. // octalConversion
  4. //
  5. // Created by Kevin Bradley on 6/28/18.
  6. // Copyright © 2018 nito. All rights reserved.
  7. //
  8. #import <Foundation/Foundation.h>
  9. #include <stdio.h>
  10. #include <errno.h>
  11. #include <libgen.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <unistd.h>
  15. #include <getopt.h>
  16. #import <sys/utsname.h>
  17. #import "HelperClass.h"
  18. #define OPTION_FLAGS "o:i:ld:hcr"
  19. char *progname;
  20. char *path;
  21. static struct option longopts[] = {
  22. { "octal", required_argument, NULL, 'o' },
  23. { "input", required_argument, NULL, 'i' },
  24. { "list", no_argument, NULL, 'l' },
  25. { "delete", required_argument, NULL, 'd' },
  26. { "help", no_argument, NULL, 'h' },
  27. { "clean", no_argument, NULL, 'c' },
  28. { "repackage", no_argument, NULL, 'r' },
  29. { NULL, 0, NULL, 0 }
  30. };
  31. void cmd_help(){
  32. printf("Usage: bootstrapTool [OPTIONS] BOOTSTRAP_FOLDER\n");
  33. printf("Makes various modifications to a raw bootstrap folder\n\n");
  34. printf(" -h, --help\t\t\tprints usage information\n");
  35. printf(" -i, --input\t\t\tthe deb package to process\n");
  36. printf(" -d, --delete\t\t\tthe package to delete\n");
  37. printf(" -l, --list\t\t\tlist all the packages installed on this bootstrap\n");
  38. printf(" -c, --clean\t\t\tclean a bad status file from duplicate entries\n");
  39. printf(" -r, --repackage\t\trepackage to fix bad locations and to change architecture\n");
  40. printf("\n");
  41. }
  42. int main(int argc, const char * argv[]) {
  43. @autoreleasepool {
  44. setuid(0);
  45. setgid(0);
  46. progname = basename(argv[0]);
  47. path = dirname(argv[0]);
  48. int flag;
  49. NSString *octalFile = nil;
  50. NSString *debFile = nil;
  51. NSString *bootstrapPath = nil;
  52. NSString *deletePackage = nil;
  53. BOOL listPackage = FALSE;
  54. BOOL repackage = FALSE;
  55. BOOL clean = FALSE;
  56. while ((flag = getopt_long(argc, argv, OPTION_FLAGS, longopts, NULL)) != -1) {
  57. switch(flag) {
  58. case 'o':
  59. octalFile = [NSString stringWithUTF8String:optarg];
  60. break;
  61. case 'i':
  62. debFile = [NSString stringWithUTF8String:optarg];
  63. break;
  64. case 'l':
  65. listPackage = TRUE;
  66. break;
  67. case 'd':
  68. deletePackage = [NSString stringWithUTF8String:optarg];
  69. break;
  70. case 'r':
  71. repackage = TRUE;
  72. break;
  73. case 'c':
  74. clean = TRUE;
  75. break;
  76. default:
  77. cmd_help();
  78. return -1;
  79. }
  80. }
  81. if (argc-optind == 1) {
  82. argc -= optind;
  83. argv += optind;
  84. bootstrapPath = [NSString stringWithUTF8String:argv[0]];
  85. NSLog(@"bootstrap path: %@?", bootstrapPath);
  86. //return 0;
  87. }
  88. if (repackage == TRUE && debFile) {
  89. DLog(@"\n [INFO] Repackaging file: %@...\n", debFile);
  90. InputPackage *output = [HelperClass packageForDeb:debFile];
  91. [output repackageInCurrentDirectoryWithArch:@"appletvos-arm64"];
  92. return 0;
  93. }
  94. //DLog(@"folder: %@ project: %@", folder, project);
  95. if (clean && bootstrapPath) {
  96. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  97. NSString *listContents = [NSString stringWithContentsOfFile:statusFile encoding:NSUTF8StringEncoding error:nil];
  98. //clean up any errant spaces
  99. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  100. listContents = [listContents stringByReplacingOccurrencesOfString:@"\n\n\n\n" withString:@"\n\n"];
  101. [listContents writeToFile:statusFile atomically:TRUE];
  102. DLog(@"\n [INFO] Cleaning status file: %@", statusFile);
  103. NSString *testStatusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  104. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  105. __block NSMutableString *newStatusFile = [NSMutableString new];
  106. __block NSMutableArray *alreadyAdded = [NSMutableArray new];
  107. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  108. DLog(@"\n [INFO] Processing pacakge: %@", obj.package);
  109. if (![alreadyAdded containsObject:obj.package]){
  110. //duplicate check first
  111. NSArray *doubleCheck = [installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", obj.package]];
  112. if (doubleCheck.count > 1){
  113. NSSortDescriptor *sortDesc = [NSSortDescriptor sortDescriptorWithKey:@"version" ascending:FALSE];
  114. StatusPackageModel *latestVersion = [[doubleCheck sortedArrayUsingDescriptors:@[sortDesc]] firstObject];
  115. DLog(@"\n [INFO] We found a duplicate package, choosing latestVersion: %@", latestVersion);
  116. [newStatusFile appendFormat:@"%@\n\n", latestVersion.rawString];
  117. [alreadyAdded addObject:obj.package];
  118. } else {
  119. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  120. }
  121. } else {
  122. DLog(@"\n [INFO] We already had this package, skipping older duplicate: %@\n", obj.package);
  123. }
  124. }];
  125. testStatusFile = [testStatusFile stringByReplacingOccurrencesOfString:@"\n\n\n" withString:@"\n\n"];
  126. DLog(@"\n Writing new output file: %@\n", testStatusFile);
  127. [newStatusFile writeToFile:testStatusFile atomically:TRUE];
  128. DLog(@"\n Done!\n\n");
  129. return 0;
  130. } else if (clean) {
  131. DLog(@"\nYou need to choose a bootstrap path to clean!\n\n");
  132. return 1;
  133. }
  134. if (deletePackage && bootstrapPath) {
  135. __block NSString *postRmFile = nil;
  136. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  137. NSArray <StatusPackageModel*> *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  138. StatusPackageModel *model = [[installedPackages filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"package == %@", deletePackage]] lastObject];
  139. if (model) {
  140. NSString *listName = [NSString stringWithFormat:@"%@.list", deletePackage];
  141. NSString *infoPath = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/info"];
  142. NSString *listFile = [infoPath stringByAppendingPathComponent:listName];
  143. NSArray *listFiles = [FM contentsOfDirectoryAtPath:infoPath error:nil];
  144. NSPredicate *blockPred = [NSPredicate predicateWithBlock:^BOOL(id _Nullable evaluatedObject, NSDictionary<NSString *,id> * _Nullable bindings) {
  145. if ([evaluatedObject containsString:deletePackage]){
  146. return TRUE;
  147. }
  148. return FALSE;
  149. }];
  150. NSArray *relatedFiles = [listFiles filteredArrayUsingPredicate:blockPred]; //any files in dpkg/info/deletePackage.*
  151. DLog(@"\nWe have found the package, determining list details from: %@", listFile);
  152. NSString *listContents = [NSString stringWithContentsOfFile:listFile encoding:NSUTF8StringEncoding error:nil];
  153. DLog(@"\nContents: %@\n", listContents);
  154. DLog(@"\nFound package info files: %@\n", relatedFiles);
  155. NSArray *files = [listContents componentsSeparatedByString:@"\n"];
  156. __block NSMutableArray *filesToProcess = [NSMutableArray new];
  157. [relatedFiles enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  158. NSString *fullPath = [infoPath stringByAppendingPathComponent:obj];
  159. if([FM fileExistsAtPath:fullPath]){
  160. if ([[obj pathExtension] isEqualToString:@"postrm"]){
  161. postRmFile = fullPath;
  162. }
  163. [filesToProcess addObject:fullPath];
  164. }
  165. }];
  166. [files enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  167. NSString *fullPath = [bootstrapPath stringByAppendingPathComponent:obj];
  168. BOOL isDir = FALSE;
  169. if([FM fileExistsAtPath:fullPath isDirectory:&isDir]){
  170. if (!isDir){
  171. DLog(@"found file: %@", fullPath);
  172. [filesToProcess addObject:fullPath];
  173. }
  174. }
  175. }];
  176. DLog(@"\nFiles to process: %@\n", filesToProcess);
  177. NSString *error = [NSString stringWithFormat:@" [WARNING] We are about to delete '%@' from the bootstrap, this cannot be undone!", deletePackage];
  178. if(![HelperClass shouldContinueWithError:error]){
  179. return -1;
  180. }
  181. [filesToProcess enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  182. [FM removeItemAtPath:obj error:nil];
  183. }];
  184. //TODO: delete the files here!
  185. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  186. [FM copyItemAtPath:statusFile toPath:[statusFile stringByAppendingPathExtension:@"bak"] error:nil];
  187. __block NSMutableString *newStatusFile = [NSMutableString new];
  188. [installedPackages enumerateObjectsUsingBlock:^(StatusPackageModel * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
  189. if (obj != model){
  190. [newStatusFile appendFormat:@"%@\n\n", obj.rawString];
  191. } else {
  192. DLog(@"skipping the model we want to delete: %@", obj);
  193. }
  194. }];
  195. [newStatusFile writeToFile:statusFile atomically:TRUE];
  196. if (postRmFile) {
  197. DLog(@"\n [WARNING] We found a postrm file, will not run this due to potential unexpected consequences off of the target device, displaying contents so you can determine if any additional steps are necessary\n");
  198. DLog(@"\n\n%@ contents:\n%@\n\n", postRmFile, [NSString stringWithContentsOfFile:postRmFile encoding:NSUTF8StringEncoding error:nil]);
  199. }
  200. }
  201. return 0;
  202. } else if (deletePackage) {
  203. DLog(@"\nYou need to choose a bootstrap path to delete from!\n\n");
  204. return 1;
  205. }
  206. if (octalFile) {
  207. NSString *octal = [HelperClass octalFromSymbols:octalFile];
  208. DLog(@"%@", octal);
  209. return 0;
  210. }
  211. if (listPackage && bootstrapPath) {
  212. NSString *statusFile = [bootstrapPath stringByAppendingPathComponent:@"Library/dpkg/status"];
  213. NSArray *installedPackages = [HelperClass statusInstalledPackagesFromFile:statusFile];
  214. DLog(@"%@", installedPackages);
  215. return 0;
  216. } else if (listPackage) {
  217. DLog(@"\nYou need to choose a bootstrap path to list packages from!\n\n");
  218. return 1;
  219. }
  220. if (debFile && bootstrapPath) {
  221. DLog(@"\nProcessing file: %@\n", debFile);
  222. InputPackage *output = [HelperClass packageForDeb:debFile];
  223. return [output installToBootstrapPath:bootstrapPath];
  224. } else if (debFile) {
  225. DLog(@"\nYou need to choose a bootstrap path to install the package to!\n\n");
  226. return 1;
  227. }
  228. //if we got this far.. nothing happened, print help.
  229. cmd_help();
  230. }
  231. return 0;
  232. }