main.m 15 KB

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