convert_archive.ck 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /*++
  2. Copyright (c) 2017 Minoca Corp.
  3. This file is licensed under the terms of the GNU General Public License
  4. version 3. Alternative licensing terms are available. Contact
  5. info@minocacorp.com for details. See the LICENSE file at the root of this
  6. project for complete licensing information.
  7. Module Name:
  8. convert_archive.ck
  9. Abstract:
  10. This module implements the convert-archive command, a sideband command used
  11. to convert other archives into .cpio.lz archives.
  12. Author:
  13. Evan Green 29-Jun-2017
  14. Environment:
  15. Chalk
  16. --*/
  17. //
  18. // ------------------------------------------------------------------- Includes
  19. //
  20. from getopt import gnuGetopt;
  21. import os;
  22. from santa.config import config;
  23. from santa.file import mkdir, rmtree;
  24. from santa.lib.archive import Archive;
  25. from spawn import ChildProcess, OPTION_SHELL;
  26. //
  27. // --------------------------------------------------------------------- Macros
  28. //
  29. //
  30. // ---------------------------------------------------------------- Definitions
  31. //
  32. //
  33. // ------------------------------------------------------ Data Type Definitions
  34. //
  35. //
  36. // ----------------------------------------------- Internal Function Prototypes
  37. //
  38. function
  39. determineArchiveFormat (
  40. input
  41. );
  42. function
  43. getArchiveFormat (
  44. format
  45. );
  46. //
  47. // -------------------------------------------------------------------- Globals
  48. //
  49. var description = "Sideband command to convert archives into .cpio.lz format";
  50. var shortOptions = "C:ef:ho:v";
  51. var longOptions = [
  52. "directory=",
  53. "format=",
  54. "help",
  55. "ignore-errors",
  56. "output=",
  57. "verbose"
  58. ];
  59. var usage =
  60. "usage: santa convert-archive [options] input_archive [output_archive]\n"
  61. "This sideband command is useful for converting archives in other \n"
  62. "formats into .cpio.lz archives, the native archive type used by Santa.\n"
  63. "Options are:\n"
  64. " -C, --directory=dir -- Use the given directory for temporary "
  65. "extraction.\n"
  66. " -e, --ignore-errors -- Ignore errors from the run commands.\n"
  67. " -f, --format=format -- Supply the file extensions of the input \n"
  68. " archive, in case they cannot be guessed based on the input file \n"
  69. " name. Examples include .tar.gz or .cpio.bz2"
  70. " -o, --output=file -- Specifies the output file name.\n"
  71. " -v, --verbose -- Print out more information about what's going on.\n"
  72. " -h, --help -- Print this help text.\n";
  73. var convertArchiveFormats = {
  74. "tar": ["tar", "none"],
  75. "tar.gz": ["tar", "gz"],
  76. "tgz": ["tar", "gz"],
  77. "tar.xz": ["tar", "xz"],
  78. "tar.bz2": ["tar", "bz2"],
  79. };
  80. //
  81. // ------------------------------------------------------------------ Functions
  82. //
  83. function
  84. command (
  85. args
  86. )
  87. /*++
  88. Routine Description:
  89. This routine implements the convert-archive command.
  90. Arguments:
  91. args - Supplies the arguments to the function.
  92. Return Value:
  93. Returns an exit code.
  94. --*/
  95. {
  96. var commandLine;
  97. var compressOption;
  98. var archive;
  99. var argc;
  100. var directory;
  101. var inputPath;
  102. var ignoreErrors = false;
  103. var format;
  104. var mode = "r";
  105. var name;
  106. var options = gnuGetopt(args[1...-1], shortOptions, longOptions);
  107. var outputPath;
  108. var process;
  109. var status;
  110. var tempdir;
  111. var value;
  112. var verbose = false;
  113. args = options[1];
  114. argc = args.length();
  115. options = options[0];
  116. for (option in options) {
  117. name = option[0];
  118. value = option[1];
  119. if ((name == "-C") || (name == "--directory")) {
  120. directory = value;
  121. } else if ((name == "-f") || (name == "--format")) {
  122. format = value;
  123. } else if ((name == "-e") || (name == "--ignore-errors")) {
  124. ignoreErrors = true;
  125. } else if ((name == "-h") || (name == "--help")) {
  126. Core.print(usage);
  127. return 1;
  128. } else if ((name == "-o") || (name == "--output")) {
  129. outputPath = value;
  130. } else if ((name == "-v") || (name == "--verbose")) {
  131. verbose = true;
  132. } else {
  133. Core.raise(ValueError("Invalid option '%s'" % name));
  134. }
  135. }
  136. if (args.length() < 1) {
  137. Core.raise(ValueError("Expected an input archive."));
  138. } else {
  139. inputPath = args[0];
  140. if (args.length() >= 2) {
  141. outputPath = args[1];
  142. if (args.length() > 2) {
  143. Core.raise(ValueError("Expected at most 2 arguments"));
  144. }
  145. }
  146. }
  147. //
  148. // Determine the input format.
  149. //
  150. if (format) {
  151. format = determineArchiveFormat(format);
  152. } else {
  153. format = determineArchiveFormat(inputPath);
  154. }
  155. //
  156. // Create the temporary working directory.
  157. //
  158. if (directory == null) {
  159. directory = "convert%d" % (os.getpid)();
  160. if (verbose) {
  161. Core.print("Creating %s" % directory);
  162. }
  163. mkdir(directory);
  164. tempdir = directory;
  165. }
  166. if (format[0] == "tar") {
  167. compressOption = "";
  168. if (format[1] == "gz") {
  169. compressOption = "-z ";
  170. } else if (format[1] == "xz") {
  171. compressOption = "-J ";
  172. } else if (format[1] == "bzip2") {
  173. compressOption = "-j ";
  174. } else if (format[1] != "none") {
  175. Core.raise(ValueError("Unknown compression format '%s'" %
  176. format[1]));
  177. }
  178. commandLine = "tar -x %s-C %s -f %s" %
  179. [compressOption, directory, inputPath];
  180. } else {
  181. Core.raise(ValueError("Unknown archive format '%s'" % format[0]));
  182. }
  183. if (verbose) {
  184. Core.print("Extracting: %s" % commandLine);
  185. }
  186. //
  187. // Run the process to extract.
  188. //
  189. process = ChildProcess(commandLine);
  190. process.options |= OPTION_SHELL;
  191. process.launch();
  192. status = process.wait(-1);
  193. if (verbose) {
  194. Core.print("Extraction finished with status %d" % status);
  195. }
  196. if ((!ignoreErrors) && (status != 0)) {
  197. Core.print("Extraction command: %s" % commandLine);
  198. Core.print("Extraction directory: %s" % directory);
  199. Core.print("Error: Extraction failed with return code %d" % status);
  200. return status;
  201. }
  202. if (outputPath == null) {
  203. outputPath = format[2];
  204. }
  205. //
  206. // Create an archive and add the members.
  207. //
  208. if (verbose) {
  209. Core.print("Creating archive %s" % outputPath);
  210. }
  211. archive = Archive.open(outputPath, "w");
  212. archive.add(directory, "");
  213. archive.close();
  214. if (verbose) {
  215. Core.print("Finished creating %s" % outputPath);
  216. }
  217. if (tempdir) {
  218. if (verbose) {
  219. Core.print("Removing temporary directory: %s" % tempdir);
  220. }
  221. rmtree(tempdir);
  222. }
  223. if (verbose) {
  224. Core.print("Finished");
  225. }
  226. return 0;
  227. }
  228. //
  229. // --------------------------------------------------------- Internal Functions
  230. //
  231. function
  232. determineArchiveFormat (
  233. input
  234. )
  235. /*++
  236. Routine Description:
  237. This routine converts an input file path into a list of well known formats.
  238. Arguments:
  239. input - Supplies the input file path, or the input format.
  240. Return Value:
  241. Returns a list of [archive, compression, output] format strings.
  242. Raises an exception if not recognized.
  243. --*/
  244. {
  245. var left = null;
  246. var originalInput = input;
  247. var result;
  248. var split;
  249. //
  250. // Try the whole thing.
  251. //
  252. result = getArchiveFormat(input);
  253. if (result) {
  254. return [result[0], result[1], null];
  255. }
  256. //
  257. // Loop chopping up to the first dot.
  258. //
  259. split = input.split(".", 1);
  260. left = null;
  261. while (split.length() > 1) {
  262. input = split[1];
  263. if (left == null) {
  264. left = split[0];
  265. } else {
  266. left = "%s.%s" % [left, split[0]];
  267. }
  268. result = getArchiveFormat(split[1]);
  269. if (result) {
  270. return [result[0], result[1], "%s.cpio.lz" % left];
  271. }
  272. split = input.split(".", 1);
  273. }
  274. Core.raise(ValueError("Archive format cannot be determined from '%s'" %
  275. originalInput));
  276. }
  277. function
  278. getArchiveFormat (
  279. format
  280. )
  281. /*++
  282. Routine Description:
  283. This routine converts a potential ending into a list of well known formats.
  284. Arguments:
  285. format - Supplies the format string, without a leading ., but with any
  286. separating dots.
  287. Return Value:
  288. Returns a list of [archive, compression] format strings.
  289. null if not recognized.
  290. --*/
  291. {
  292. var entry;
  293. try {
  294. entry = convertArchiveFormats[format];
  295. } except KeyError {
  296. return null;
  297. }
  298. return entry;
  299. }