From 956b7cca7e61c79b5bb11f4ef08055a3520aa96e Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 00:51:52 +0200 Subject: [PATCH 01/34] Migrate from clize to docopt --- arm_now/arm_now.py | 93 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 16 deletions(-) diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 90a9c8d..5bd24da 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -1,6 +1,55 @@ #!/usr/bin/env python3 # -*- coding: utf-8 -*- +#  ================ Start Argument Parsing ============================== +"""arm_now. +Usage: + arm_now list [--all] + arm_now start [arch] [--clean] [--sync] [--offline] [--redir=]... + arm_now clean + arm_now resize [--correct] + arm_now install [arch] [--clean] + arm_now -h | --help + arm_now --version +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Commands: + list List all available images for all cpu. + start Start a vm with a cpu. (default: armv5-eabi) + resize Resize the current rootfs. (example: resize 1G) + clean Delete the current rootfs. + install Install and config a rootfs for the given . (default: armv5-eabi) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Options: + --sync Synchronize the current directory with the vm home. + --redir protocol:host::guest Redirect the host port to the guest (example: --redir tcp:8000::80) + --clean Clean the current image before starting. + --offline Start with zero internet request. + --correct Correct the filesystem. + -h --help Show this screen. + --version Show version. + +Defaults: + arch Defaults to armv5-eabi. +""" + +from docopt import docopt + +def main(): + a = docopt(__doc__, version='arm_now 0.1') + if a["list"]: + do_list(a["--all"]) + elif a["start"]: + do_start(a[""] or "armv5-eabi", + a["--clean"], a["--sync"], a["--offline"], a["--redir"]) + elif a["clean"]: + do_clean() + elif a["resize"]: + do_resize(a[""], a["--correct"]) + elif a["install"]: + do_install(a["arch"] or "armv5-eabi", a["--clean"]) + +#  ================ End Argument Parsing ============================== + import urllib.request import requests import sys @@ -187,9 +236,11 @@ def is_already_created(arch): do_clean() return False -def install(arch): +def do_install(arch, clean=False): """ download and setup filesystem and kernel """ + if clean: + do_clean() if arch not in qemu_options: print("ERROR: I don't know this arch yet", file=sys.stderr) print("maybe you meant: {}".format(maybe_you_meant(arch, qemu_options.keys()) or qemu_options.keys()), file=sys.stderr) @@ -209,6 +260,7 @@ def install(arch): download(rootfs, ROOTFS) with open(DIR + "/arch", "w") as F: F.write(arch) + print("[+] Installed") def avoid_parameter_injection(params): new_params = [] @@ -262,6 +314,7 @@ def config_filesystem(rootfs, arch): export PATH=$PATH:/opt/bin:/opt/sbin """) +@exall(subprocess.check_call, subprocess.CalledProcessError, print_error) def add_local_files(rootfs, dest): filemagic = magic.from_file(rootfs) if "ext2" not in filemagic: @@ -270,7 +323,7 @@ def add_local_files(rootfs, dest): # TODO: check rootfs fs against parameter injection ext2_write_to_file(rootfs, "/sbin/save", "cd /root\ntar cf /root.tar *\nsync\n") - print("Adding current directory to the filesystem..") + print("[+] Adding current directory to the filesystem..") with tempfile.TemporaryDirectory() as tmpdirname: files = [ i for i in os.listdir(".") if i != "arm_now" and not i.startswith("-") ] if files: @@ -332,7 +385,7 @@ def parse_redir(redir): raise clize.ArgumentError("Invalid argument: --redir {}".format(r)) return ''.join(map("-redir {} ".format, redir)) -def start(arch="", *, clean=False, sync=False, offline=False, redir:(clize.parameters.multi(min=0, max=3))): +def do_start(arch, clean, sync, offline, redir): """Setup and start a virtualmachine using qemu. :param arch: The cpu architecture that will be started. @@ -341,6 +394,12 @@ def start(arch="", *, clean=False, sync=False, offline=False, redir:(clize.param :param clean: Clean filesystem before starting. :param sync: Sync le current directory with the guest. """ + print("start") + print(f"arch {arch}") + print(f"clean {clean}") + print(f"sync {sync}") + print(f"offline {offline}") + print(f"redir {redir}") redir = parse_redir(redir) if not arch: print("Supported architectures:") @@ -350,7 +409,7 @@ def start(arch="", *, clean=False, sync=False, offline=False, redir:(clize.param if clean: do_clean() if not offline: - install(arch) + do_install(arch) config_filesystem(ROOTFS, arch) if sync: add_local_files(ROOTFS, "/root") @@ -367,10 +426,22 @@ def do_clean(): os.unlink(ROOTFS) shutil.rmtree(DIR, ignore_errors=True) -def do_resize(size): +class Color: + orange = "\x1B[33m" + green = "\x1B[32m" + red = "\x1B[31m" + normal = "\x1B[0m" + +def do_resize(size, correct): """ Resize filesystem. """ subprocess.check_call(["qemu-img", "resize", ROOTFS, size]) + subprocess.check_call(["e2fsck", "-fy", ROOTFS]) + subprocess.check_call(["ls", "-lh", ROOTFS]) + print("{c.green}[+] Resized to {size}{c.normal}".format(size=size, c=Color)) + if correct: + print("{c.orange}[+] Correcting ... (be patient){c.normal}".format(size=size, c=Color)) + subprocess.check_call("mke2fs -F -b 1024 -m 0 -g 272".split() + [ROOTFS]) def test_arch(arch): arch = arch[:-1] @@ -378,7 +449,7 @@ def test_arch(arch): if kernel and rootfs: print("{}: OK".format(arch)) -def list_arch(*, all=False): +def do_list(all=False): """ List all compactible cpu architecture """ if not all: @@ -389,15 +460,5 @@ def list_arch(*, all=False): p = Pool(10) ret = p.map(test_arch, all_arch) -# @exall(clize.run, Exception, print_error) -def main(): - clize.run({ - "start": start, - "clean": do_clean, - "list": list_arch, - "resize": do_resize, - "install": install - }) - if __name__ == "__main__": main() From d9e6a6afcebdf845f2b9257865b64a2794b75647 Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 01:20:48 +0200 Subject: [PATCH 02/34] correct resize option --- arm_now/arm_now.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 5bd24da..9fc43c7 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -414,6 +414,12 @@ def do_start(arch, clean, sync, offline, redir): if sync: add_local_files(ROOTFS, "/root") run(arch, KERNEL, DTB, ROOTFS, redir) + try: + subprocess.check_call(["e2fsck", "-vfy", ROOTFS]) + except subprocess.CalledProcessError as e: + print(e) + if str(e).find("returned non-zero exit status 1."): + print("{c.orange}It's ok but next time poweroff{c.normal}".format(c=Color)) if sync: get_local_files(ROOTFS, "/root.tar", ".") @@ -436,6 +442,7 @@ def do_resize(size, correct): """ Resize filesystem. """ subprocess.check_call(["qemu-img", "resize", ROOTFS, size]) + subprocess.check_call(["resize2fs", ROOTFS]) subprocess.check_call(["e2fsck", "-fy", ROOTFS]) subprocess.check_call(["ls", "-lh", ROOTFS]) print("{c.green}[+] Resized to {size}{c.normal}".format(size=size, c=Color)) From 29c6a8ce5628dd53ded1796c489f0c85199ee7c3 Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 20:08:44 +0200 Subject: [PATCH 03/34] move all utils functions to utils.py --- arm_now/arm_now.py | 106 +++++-------------------------------------- arm_now/utils.py | 109 +++++++++++++++++++++++++++++++++++++++++++++ dev-doc.md | 5 +++ 3 files changed, 125 insertions(+), 95 deletions(-) create mode 100644 arm_now/utils.py create mode 100644 dev-doc.md diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 9fc43c7..6867e5c 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -5,10 +5,10 @@ """arm_now. Usage: arm_now list [--all] - arm_now start [arch] [--clean] [--sync] [--offline] [--redir=]... + arm_now start [] [--clean] [--sync] [--offline] [--redir=]... arm_now clean arm_now resize [--correct] - arm_now install [arch] [--clean] + arm_now install [] [--clean] arm_now -h | --help arm_now --version ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -46,7 +46,7 @@ def main(): elif a["resize"]: do_resize(a[""], a["--correct"]) elif a["install"]: - do_install(a["arch"] or "armv5-eabi", a["--clean"]) + do_install(a[""] or "armv5-eabi", a["--clean"]) #  ================ End Argument Parsing ============================== @@ -63,15 +63,13 @@ def main(): import operator import subprocess import platform -import tempfile import re # once cpio fully supported will we still need this magic ? import magic -from pySmartDL import SmartDL -import difflib import clize +from utils import * from exall import exall, ignore, print_warning, print_traceback, print_error DOWNLOAD_CACHE_DIR = "/tmp/arm_now" @@ -125,28 +123,6 @@ def main(): DTB = DIR + "dtb" ROOTFS = DIR + "rootfs.ext2" -def maybe_you_meant(string, strings): - return ' or '.join(difflib.get_close_matches(string, strings, cutoff=0.3)) - -@exall(os.mkdir, FileExistsError, ignore) -def download(url, filename): - print("\nDownloading {} from {}".format(filename, url)) - filename_cache = url.split('/')[-1] - filename_cache = ''.join([ c for c in filename_cache if c.isdigit() or c.isalpha() ]) - filename_cache = DOWNLOAD_CACHE_DIR + "/" + filename_cache - print(filename_cache) - if os.path.exists(filename): - print("Filexists") - elif os.path.exists(filename_cache): - print("Already downloaded") - shutil.copyfile(filename_cache, filename) - else: - os.mkdir(DOWNLOAD_CACHE_DIR) - # wget.download(url, out=filename_cache) - obj = SmartDL(url, filename_cache) - obj.start() - shutil.copyfile(filename_cache, filename) - def indexof_parse(url): re_href = re.compile('\[DIR\].*href="?([^ <>"]*)"?') response = requests.get(url) @@ -254,34 +230,14 @@ def do_install(arch, clean=False): return with contextlib.suppress(FileExistsError): os.mkdir(DIR) - download(kernel, KERNEL) + download(kernel, KERNEL, DOWNLOAD_CACHE_DIR) if dtb: - download(dtb, DTB) - download(rootfs, ROOTFS) + download(dtb, DTB, DOWNLOAD_CACHE_DIR) + download(rootfs, ROOTFS, DOWNLOAD_CACHE_DIR) with open(DIR + "/arch", "w") as F: F.write(arch) print("[+] Installed") -def avoid_parameter_injection(params): - new_params = [] - for p in params: - if p.startswith("-"): - print("WARNING: parameter injection detected, '{}' will be ingored".format(p)) - else: - new_params.append(p) - return new_params - -def ext2_write_to_file(rootfs, dest, script): - with tempfile.TemporaryDirectory() as tmpdirname: - filename = tmpdirname + "/script" - print(filename) - with open(filename, "w") as F: - F.write(script) - subprocess.check_call("e2cp -G 0 -O 0 -P 555".split(' ') + [filename, rootfs + ":" + dest]) - -def ext2_rm(rootfs, filename): - subprocess.check_call(["e2rm", rootfs + ":" + filename]) - def config_filesystem(rootfs, arch): filemagic = magic.from_file(rootfs) if "ext2" not in filemagic: @@ -314,28 +270,6 @@ def config_filesystem(rootfs, arch): export PATH=$PATH:/opt/bin:/opt/sbin """) -@exall(subprocess.check_call, subprocess.CalledProcessError, print_error) -def add_local_files(rootfs, dest): - filemagic = magic.from_file(rootfs) - if "ext2" not in filemagic: - print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) - return - # TODO: check rootfs fs against parameter injection - ext2_write_to_file(rootfs, "/sbin/save", - "cd /root\ntar cf /root.tar *\nsync\n") - print("[+] Adding current directory to the filesystem..") - with tempfile.TemporaryDirectory() as tmpdirname: - files = [ i for i in os.listdir(".") if i != "arm_now" and not i.startswith("-") ] - if files: - tar = tmpdirname + "/current_directory.tar" - subprocess.check_call(["tar", "cf", tar] + files) - subprocess.check_call("e2cp -G 0 -O 0".split(' ') + [tar, rootfs + ":/"]) - ext2_write_to_file(rootfs, "/etc/init.d/S95_sync_current_diretory",""" - cd /root - tar xf /current_directory.tar - rm /current_directory.tar - """) - @exall(subprocess.check_call, subprocess.CalledProcessError, print_warning) def get_local_files(rootfs, src, dest): filemagic = magic.from_file(rootfs) @@ -349,19 +283,6 @@ def get_local_files(rootfs, src, dest): else: print("Use the 'save' command before exiting the vm to retrieve all files on the host") -distribution = platform.linux_distribution()[0].lower() - -def which(filename, **kwargs): - try: - subprocess.check_output(["which", filename]) - return True - except subprocess.CalledProcessError: - if distribution in kwargs: - print(kwargs[distribution]) - else: - print(kwargs["ubuntu"]) - return False - def check_dependencies(): dependencies = [ which("e2cp", ubuntu="apt-get install e2tools", arch="yaourt -S e2tools"), @@ -415,11 +336,12 @@ def do_start(arch, clean, sync, offline, redir): add_local_files(ROOTFS, "/root") run(arch, KERNEL, DTB, ROOTFS, redir) try: + print(" Checking the filesystem ".center(80, "+")) subprocess.check_call(["e2fsck", "-vfy", ROOTFS]) except subprocess.CalledProcessError as e: print(e) if str(e).find("returned non-zero exit status 1."): - print("{c.orange}It's ok but next time poweroff{c.normal}".format(c=Color)) + porange("It's ok but next time poweroff") if sync: get_local_files(ROOTFS, "/root.tar", ".") @@ -432,12 +354,6 @@ def do_clean(): os.unlink(ROOTFS) shutil.rmtree(DIR, ignore_errors=True) -class Color: - orange = "\x1B[33m" - green = "\x1B[32m" - red = "\x1B[31m" - normal = "\x1B[0m" - def do_resize(size, correct): """ Resize filesystem. """ @@ -445,9 +361,9 @@ def do_resize(size, correct): subprocess.check_call(["resize2fs", ROOTFS]) subprocess.check_call(["e2fsck", "-fy", ROOTFS]) subprocess.check_call(["ls", "-lh", ROOTFS]) - print("{c.green}[+] Resized to {size}{c.normal}".format(size=size, c=Color)) + pgreen("[+] Resized to {size}".format(size=size)) if correct: - print("{c.orange}[+] Correcting ... (be patient){c.normal}".format(size=size, c=Color)) + porange("[+] Correcting ... (be patient)".format(size=size)) subprocess.check_call("mke2fs -F -b 1024 -m 0 -g 272".split() + [ROOTFS]) def test_arch(arch): diff --git a/arm_now/utils.py b/arm_now/utils.py new file mode 100644 index 0000000..3347e74 --- /dev/null +++ b/arm_now/utils.py @@ -0,0 +1,109 @@ +import functools +import subprocess +import os +import shutil +import difflib +import tempfile + +from pySmartDL import SmartDL +from exall import exall, ignore, print_warning, print_traceback, print_error + +""" +Utils functions: + pcolor: Print with beautiful colors: porange, pgreen, pred. + distribution: Return the current distribution name. + which: Test if the command is installed. + maybe_you_meant: Smart correction of user provided input. + avoid_parameter_injection: Security check on user provided input. + ext2_write_to_file: Add a file to an existing ext2 image. + add_local_files: Add multiple files to an existing ex2 image. + ext2_rm: Delete a file from an existing ext2 image. +""" + +def pcolor(color, *kargs, **kwargs): + print(color,end="") + print(*kargs, **kwargs,end="") + print("\x1B[0m") + +porange = functools.partial(pcolor, "\x1B[33m") +pgreen = functools.partial(pcolor, "\x1B[32m") +pred = functools.partial(pcolor, "\x1B[31m") + +@functools.lru_cache() +def distribution(): + return platform.linux_distribution()[0].lower() + +def which(filename, **kwargs): + try: + subprocess.check_output(["which", filename]) + return True + except subprocess.CalledProcessError: + if distribution() in kwargs: + print(kwargs[distribution()]) + else: + print(kwargs["ubuntu"]) + return False + +def maybe_you_meant(string, strings): + return ' or '.join(difflib.get_close_matches(string, strings, cutoff=0.3)) + +@exall(os.mkdir, FileExistsError, ignore) +def download(url, filename, cache_directory): + print("\nDownloading {} from {}".format(filename, url)) + filename_cache = url.split('/')[-1] + filename_cache = ''.join([ c for c in filename_cache if c.isdigit() or c.isalpha() ]) + filename_cache = cache_directory + "/" + filename_cache + print(filename_cache) + if os.path.exists(filename): + print("Filexists") + elif os.path.exists(filename_cache): + print("Already downloaded") + shutil.copyfile(filename_cache, filename) + else: + os.mkdir(cache_directory) + # wget.download(url, out=filename_cache) + obj = SmartDL(url, filename_cache) + obj.start() + shutil.copyfile(filename_cache, filename) + +def avoid_parameter_injection(params): + new_params = [] + for p in params: + if p.startswith("-"): + print("WARNING: parameter injection detected, '{}' will be ingored".format(p)) + else: + new_params.append(p) + return new_params + +def ext2_write_to_file(rootfs, dest, script): + with tempfile.TemporaryDirectory() as tmpdirname: + filename = tmpdirname + "/script" + print(filename) + with open(filename, "w") as F: + F.write(script) + subprocess.check_call("e2cp -G 0 -O 0 -P 555".split(' ') + [filename, rootfs + ":" + dest]) + +@exall(subprocess.check_call, subprocess.CalledProcessError, print_error) +def add_local_files(rootfs, dest): + filemagic = magic.from_file(rootfs) + if "ext2" not in filemagic: + print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) + return + # TODO: check rootfs fs against parameter injection + ext2_write_to_file(rootfs, "/sbin/save", + "cd /root\ntar cf /root.tar *\nsync\n") + print("[+] Adding current directory to the filesystem..") + with tempfile.TemporaryDirectory() as tmpdirname: + files = [ i for i in os.listdir(".") if i != "arm_now" and not i.startswith("-") ] + if files: + tar = tmpdirname + "/current_directory.tar" + subprocess.check_call(["tar", "cf", tar] + files) + subprocess.check_call("e2cp -G 0 -O 0".split(' ') + [tar, rootfs + ":/"]) + ext2_write_to_file(rootfs, "/etc/init.d/S95_sync_current_diretory",""" + cd /root + tar xf /current_directory.tar + rm /current_directory.tar + """) + +def ext2_rm(rootfs, filename): + subprocess.check_call(["e2rm", rootfs + ":" + filename]) diff --git a/dev-doc.md b/dev-doc.md new file mode 100644 index 0000000..9187fdc --- /dev/null +++ b/dev-doc.md @@ -0,0 +1,5 @@ +mkdir venv +python3 -m venv venv +python3 setup.py install +# to delete untracked files !! +git clean -fdx From f75db62b3f09772ab8adf602d7f77d79e680277d Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 20:54:16 +0200 Subject: [PATCH 04/34] resize fully working --- Changelog.txt | 6 ++++++ arm_now/arm_now.py | 27 ++++++++------------------- arm_now/utils.py | 36 ++++++++++++++++++++++++++++-------- 3 files changed, 42 insertions(+), 27 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 10ff5fd..713c83e 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -25,6 +25,12 @@ Add tcp/udp --redir --- Mon Apr 23 2018 Add --offline and --resize option by @NoobieDog +--- Thu May 31 2018 +Correct --resize +Fix bug with trying to sync big files +Migrate from clize to docopt +Refactor utils functions + TODO: For --offline option check if local filesystem are avaiable otherwise exit cleanly and print a error message. For --resize option check with a regex if the parameter is correct to avoid command/parameter injection. diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 6867e5c..0042d95 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -63,10 +63,9 @@ def main(): import operator import subprocess import platform +import tempfile import re -# once cpio fully supported will we still need this magic ? -import magic import clize from utils import * @@ -206,7 +205,7 @@ def is_already_created(arch): old_arch = F.read() if old_arch == arch: return True - response = input("(use --clean next time) Current directory contains a different arch, delete ? (y/n) ") + response = input("(use --clean next time) An {} image exists, delete ? (y/n) ".format(old_arch)) if not response.startswith("y"): sys.exit(1) do_clean() @@ -218,15 +217,15 @@ def do_install(arch, clean=False): if clean: do_clean() if arch not in qemu_options: - print("ERROR: I don't know this arch yet", file=sys.stderr) - print("maybe you meant: {}".format(maybe_you_meant(arch, qemu_options.keys()) or qemu_options.keys()), file=sys.stderr) + pred("ERROR: I don't know this arch yet", file=sys.stderr) + porange("maybe you meant: {}".format(maybe_you_meant(arch, qemu_options.keys()) or qemu_options.keys()), file=sys.stderr) sys.exit(1) kernel, dtb, rootfs = scrawl_kernel(arch) if kernel is None or rootfs is None: - print("ERROR: couldn't download files for this arch", file=sys.stderr) + pred("ERROR: couldn't download files for this arch", file=sys.stderr) sys.exit(1) if is_already_created(arch): - print("WARNING: {} already exists, use --clean to restart with a fresh filesystem".format(DIR)) + porange("WARNING: {} already exists, use --clean to restart with a fresh filesystem".format(DIR)) return with contextlib.suppress(FileExistsError): os.mkdir(DIR) @@ -239,9 +238,7 @@ def do_install(arch, clean=False): print("[+] Installed") def config_filesystem(rootfs, arch): - filemagic = magic.from_file(rootfs) - if "ext2" not in filemagic: - print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) + if not is_ext2(rootfs): return try: ext2_rm(rootfs, '/etc/init.d/S40network') @@ -272,9 +269,7 @@ def config_filesystem(rootfs, arch): @exall(subprocess.check_call, subprocess.CalledProcessError, print_warning) def get_local_files(rootfs, src, dest): - filemagic = magic.from_file(rootfs) - if "ext2" not in filemagic: - print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) + if not is_ext2(rootfs): return subprocess.check_call(["e2cp", rootfs + ":" + src, dest]) if os.path.exists("root.tar"): @@ -315,12 +310,6 @@ def do_start(arch, clean, sync, offline, redir): :param clean: Clean filesystem before starting. :param sync: Sync le current directory with the guest. """ - print("start") - print(f"arch {arch}") - print(f"clean {clean}") - print(f"sync {sync}") - print(f"offline {offline}") - print(f"redir {redir}") redir = parse_redir(redir) if not arch: print("Supported architectures:") diff --git a/arm_now/utils.py b/arm_now/utils.py index 3347e74..fdc12af 100644 --- a/arm_now/utils.py +++ b/arm_now/utils.py @@ -1,10 +1,14 @@ import functools import subprocess import os +import sys import shutil import difflib import tempfile +import contextlib +# once cpio fully supported will we still need this magic ? +import magic from pySmartDL import SmartDL from exall import exall, ignore, print_warning, print_traceback, print_error @@ -21,9 +25,12 @@ """ def pcolor(color, *kargs, **kwargs): - print(color,end="") - print(*kargs, **kwargs,end="") - print("\x1B[0m") + """ proxy print arguments """ + output = sys.stdout if "file" not in kwargs else kwargs["file"] + with contextlib.redirect_stdout(output): + print(color,end="") + print(*kargs, **kwargs,end="") + print("\x1B[0m") porange = functools.partial(pcolor, "\x1B[33m") pgreen = functools.partial(pcolor, "\x1B[32m") @@ -78,16 +85,17 @@ def avoid_parameter_injection(params): def ext2_write_to_file(rootfs, dest, script): with tempfile.TemporaryDirectory() as tmpdirname: filename = tmpdirname + "/script" - print(filename) with open(filename, "w") as F: F.write(script) subprocess.check_call("e2cp -G 0 -O 0 -P 555".split(' ') + [filename, rootfs + ":" + dest]) -@exall(subprocess.check_call, subprocess.CalledProcessError, print_error) +def print_error_tips(exception): + pred("ERROR: Plz try to resize your filesystem: arm_now resize 500M") + print_error(exception) + +@exall(subprocess.check_call, subprocess.CalledProcessError, print_error_tips) def add_local_files(rootfs, dest): - filemagic = magic.from_file(rootfs) - if "ext2" not in filemagic: - print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) + if not is_ext2(rootfs): return # TODO: check rootfs fs against parameter injection ext2_write_to_file(rootfs, "/sbin/save", @@ -101,9 +109,21 @@ def add_local_files(rootfs, dest): subprocess.check_call("e2cp -G 0 -O 0".split(' ') + [tar, rootfs + ":/"]) ext2_write_to_file(rootfs, "/etc/init.d/S95_sync_current_diretory",""" cd /root + echo "[+] Syncing with the current directory... (Be patient)" + echo "Tips for big files:" + echo " Know that you only need to --sync once," + echo " because the filesystem is persistent." tar xf /current_directory.tar rm /current_directory.tar + rm /etc/init.d/S95_sync_current_diretory """) def ext2_rm(rootfs, filename): subprocess.check_call(["e2rm", rootfs + ":" + filename]) + +def is_ext2(rootfs): + filemagic = magic.from_file(rootfs) + if "ext2" not in filemagic: + print("{}\nthis filetype is not fully supported yet, but this will boot".format(filemagic)) + return False + return True From ec13879d1ad1ed6458ff238401f256b4107b3cdb Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 20:57:48 +0200 Subject: [PATCH 05/34] add utils.py --- README.md | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e3e151d..928065f 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ arm_now instantly deploys and starts a virtual machine when needed, I mainly use # Install ```sh -# pip3 install https://github.com/nongiach/arm_now/archive/master.zip +# pip3 install https://github.com/nongiach/arm_now/archive/master.zip --upgrade ``` # Start an arm Virtual Machine diff --git a/setup.py b/setup.py index e793421..b052b2e 100644 --- a/setup.py +++ b/setup.py @@ -4,7 +4,7 @@ version='1.01', author='@chaign_c', url='https://github.com/nongiach/arm_now', - packages=['arm_now'], + packages=['arm_now', 'utils'], py_modules=['arm_now'], entry_points = { 'console_scripts': [ From 80b1a53b0a96e911011f7d81faed59118cfa689a Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 20:59:40 +0200 Subject: [PATCH 06/34] utils in py_modules --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index b052b2e..89d0a50 100644 --- a/setup.py +++ b/setup.py @@ -4,8 +4,8 @@ version='1.01', author='@chaign_c', url='https://github.com/nongiach/arm_now', - packages=['arm_now', 'utils'], - py_modules=['arm_now'], + packages=['arm_now'], + py_modules=['arm_now', 'utils'], entry_points = { 'console_scripts': [ 'arm_now = arm_now:main', From 42a475af0566d65b83a98551816f165f7371ca07 Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 21:08:04 +0200 Subject: [PATCH 07/34] .utils --- arm_now/arm_now.py | 3 ++- dev-doc.md | 3 ++- setup.py | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 0042d95..ac5c44e 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -68,7 +68,7 @@ def main(): import clize -from utils import * +from .utils import * from exall import exall, ignore, print_warning, print_traceback, print_error DOWNLOAD_CACHE_DIR = "/tmp/arm_now" @@ -205,6 +205,7 @@ def is_already_created(arch): old_arch = F.read() if old_arch == arch: return True + # response = input("(use --clean next time) Current directory contains a different arch ({}), delete ? (y/n) ".format(old_arch)) response = input("(use --clean next time) An {} image exists, delete ? (y/n) ".format(old_arch)) if not response.startswith("y"): sys.exit(1) diff --git a/dev-doc.md b/dev-doc.md index 9187fdc..2f81ea8 100644 --- a/dev-doc.md +++ b/dev-doc.md @@ -1,5 +1,6 @@ mkdir venv python3 -m venv venv +pip install arm_now python3 setup.py install -# to delete untracked files !! +# to delete untracked files populated by setup install git clean -fdx diff --git a/setup.py b/setup.py index 89d0a50..d7025ec 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ author='@chaign_c', url='https://github.com/nongiach/arm_now', packages=['arm_now'], - py_modules=['arm_now', 'utils'], + py_modules=['arm_now', 'arm_now.utils'], entry_points = { 'console_scripts': [ 'arm_now = arm_now:main', From ac0dafc528b68481dcd757c1c1c05929da747387 Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 21:16:00 +0200 Subject: [PATCH 08/34] disable audio for a boot time sake --- arm_now/arm_now.py | 3 ++- setup.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index ac5c44e..a6ef93a 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -35,7 +35,7 @@ from docopt import docopt def main(): - a = docopt(__doc__, version='arm_now 0.1') + a = docopt(__doc__, version='arm_now 1.2') if a["list"]: do_list(a["--all"]) elif a["start"]: @@ -186,6 +186,7 @@ def run(arch, kernel, dtb, rootfs, redir): print("Starting qemu-system-{}".format(arch)) qemu_config = "-serial stdio -monitor /dev/null {redir}".format(redir=redir) cmd = """stty intr ^] + export QEMU_AUDIO_DRV="none" qemu-system-{arch} {options} \ -m 256M \ -nographic \ diff --git a/setup.py b/setup.py index d7025ec..bc0b59d 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, Extension setup(name='arm_now', - version='1.01', + version='1.02', author='@chaign_c', url='https://github.com/nongiach/arm_now', packages=['arm_now'], From 4c0448a3d84ba08b63a1363684c3207711e158d1 Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 21:23:01 +0200 Subject: [PATCH 09/34] test-run.sh --- Changelog.txt | 3 +++ arm_now/arm_now.py | 2 +- dev-doc.md | 2 +- test-run.sh | 6 ++++++ 4 files changed, 11 insertions(+), 2 deletions(-) create mode 100644 test-run.sh diff --git a/Changelog.txt b/Changelog.txt index 713c83e..5f4aac9 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -30,8 +30,11 @@ Correct --resize Fix bug with trying to sync big files Migrate from clize to docopt Refactor utils functions +Disable audio to gain boot speed + TODO: +Unit test For --offline option check if local filesystem are avaiable otherwise exit cleanly and print a error message. For --resize option check with a regex if the parameter is correct to avoid command/parameter injection. Search for new kernel and rootfs images, maybe I missed some of them that have ramfs inside the kernel image. diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index a6ef93a..91198f9 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -195,7 +195,7 @@ def run(arch, kernel, dtb, rootfs, redir): -no-reboot stty intr ^c """.format(arch=arch, qemu_config=qemu_config, options=options, dtb=dtb) - print(cmd) + pgreen(cmd) os.system(cmd) def is_already_created(arch): diff --git a/dev-doc.md b/dev-doc.md index 2f81ea8..1de3af6 100644 --- a/dev-doc.md +++ b/dev-doc.md @@ -1,6 +1,6 @@ mkdir venv python3 -m venv venv -pip install arm_now +pip uninstall arm_now python3 setup.py install # to delete untracked files populated by setup install git clean -fdx diff --git a/test-run.sh b/test-run.sh new file mode 100644 index 0000000..4ca8343 --- /dev/null +++ b/test-run.sh @@ -0,0 +1,6 @@ +# Commit and run this script, BUT COMMIT BEFORE if you don't want to loose change +python3 -m venv venv +pip uninstall arm_now +python3 setup.py install +# to delete untracked files populated by setup install +git clean -fdx From 1b44348941b7ce49538d95fe8aa296995df935ba Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 21:46:10 +0200 Subject: [PATCH 10/34] e2fsck before resize --- arm_now/arm_now.py | 2 +- setup.py | 2 +- test-run.sh | 9 +++++---- 3 files changed, 7 insertions(+), 6 deletions(-) mode change 100644 => 100755 test-run.sh diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 91198f9..9587bb3 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -349,8 +349,8 @@ def do_resize(size, correct): """ Resize filesystem. """ subprocess.check_call(["qemu-img", "resize", ROOTFS, size]) - subprocess.check_call(["resize2fs", ROOTFS]) subprocess.check_call(["e2fsck", "-fy", ROOTFS]) + subprocess.check_call(["resize2fs", ROOTFS]) subprocess.check_call(["ls", "-lh", ROOTFS]) pgreen("[+] Resized to {size}".format(size=size)) if correct: diff --git a/setup.py b/setup.py index bc0b59d..5021e6b 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ from setuptools import setup, Extension setup(name='arm_now', - version='1.02', + version='1.2', author='@chaign_c', url='https://github.com/nongiach/arm_now', packages=['arm_now'], diff --git a/test-run.sh b/test-run.sh old mode 100644 new mode 100755 index 4ca8343..bb0f02b --- a/test-run.sh +++ b/test-run.sh @@ -1,6 +1,7 @@ +# A dirty test script # Commit and run this script, BUT COMMIT BEFORE if you don't want to loose change -python3 -m venv venv -pip uninstall arm_now -python3 setup.py install +set -e +sudo pip uninstall arm_now +sudo python3 setup.py install # to delete untracked files populated by setup install -git clean -fdx +sudo git clean -fdx From 929478a75c68e2923db5bd7dcf30324d5332f8dc Mon Sep 17 00:00:00 2001 From: chaign_c Date: Thu, 31 May 2018 22:36:51 +0200 Subject: [PATCH 11/34] move --redir to docopt --- Changelog.txt | 2 ++ arm_now/arm_now.py | 23 ++++++++++++++--------- requirements.txt | 8 ++++---- setup.py | 2 +- 4 files changed, 21 insertions(+), 14 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index 5f4aac9..d12efb2 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -31,9 +31,11 @@ Fix bug with trying to sync big files Migrate from clize to docopt Refactor utils functions Disable audio to gain boot speed +Add colors TODO: +Add a project page describing every options, --redir .. Unit test For --offline option check if local filesystem are avaiable otherwise exit cleanly and print a error message. For --resize option check with a regex if the parameter is correct to avoid command/parameter injection. diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 9587bb3..51b3f5d 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -5,7 +5,7 @@ """arm_now. Usage: arm_now list [--all] - arm_now start [] [--clean] [--sync] [--offline] [--redir=]... + arm_now start [] [--clean] [--sync] [--offline] [--redir=]... [--add-qemu-options=] arm_now clean arm_now resize [--correct] arm_now install [] [--clean] @@ -23,6 +23,7 @@ --sync Synchronize the current directory with the vm home. --redir protocol:host::guest Redirect the host port to the guest (example: --redir tcp:8000::80) --clean Clean the current image before starting. + --add-qemu-options= Add options to qemu-system-. (example: --add-qemu-options="-no-reboot") --offline Start with zero internet request. --correct Correct the filesystem. -h --help Show this screen. @@ -40,7 +41,8 @@ def main(): do_list(a["--all"]) elif a["start"]: do_start(a[""] or "armv5-eabi", - a["--clean"], a["--sync"], a["--offline"], a["--redir"]) + a["--clean"], a["--sync"], a["--offline"], a["--redir"], + a["--add-qemu-options"]) elif a["clean"]: do_clean() elif a["resize"]: @@ -66,8 +68,6 @@ def main(): import tempfile import re -import clize - from .utils import * from exall import exall, ignore, print_warning, print_traceback, print_error @@ -278,7 +278,7 @@ def get_local_files(rootfs, src, dest): subprocess.check_call("tar xf root.tar".split(' ')) os.unlink("root.tar") else: - print("Use the 'save' command before exiting the vm to retrieve all files on the host") + pgreen("Use the 'save' command before exiting the vm to retrieve all files on the host") def check_dependencies(): dependencies = [ @@ -297,13 +297,14 @@ def parse_redir(redir): qemu_redir = [] for r in redir: if not re_redir.match(r): + pred("ERROR: Invalid argument: --redir {}".format(r)) print("example:") print("\tredirect tcp host 8000 to guest 80: --redir tcp:8000::80") print("\tredirect udp host 4444 to guest 44: --redir udp:4444::44") - raise clize.ArgumentError("Invalid argument: --redir {}".format(r)) + sys.exit(1) return ''.join(map("-redir {} ".format, redir)) -def do_start(arch, clean, sync, offline, redir): +def do_start(arch, clean, sync, offline, redir, add_qemu_options): """Setup and start a virtualmachine using qemu. :param arch: The cpu architecture that will be started. @@ -313,11 +314,15 @@ def do_start(arch, clean, sync, offline, redir): :param sync: Sync le current directory with the guest. """ redir = parse_redir(redir) + print("o" * 40) + print(add_qemu_options) + print(redir) if not arch: print("Supported architectures:") print(list_arch()) - raise clize.ArgumentError("no arch specified") - check_dependencies() + pred("ERROR: no arch specified") + sys.exit(1) + check_dependencies() if clean: do_clean() if not offline: diff --git a/requirements.txt b/requirements.txt index 65ac5da..765c05b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -exall==1.0 -requests==2.18.4 -clize==4.0.3 -pySmartDL==1.2.5 +exall>=0.11 +requests>=2.18.4 +pySmartDL>=1.2.5 +docopt>=0.6.2 diff --git a/setup.py b/setup.py index 5021e6b..a875717 100644 --- a/setup.py +++ b/setup.py @@ -14,7 +14,7 @@ install_requires=[ 'exall', 'requests', - 'clize', + 'docopt', 'pySmartDL', 'python-magic' ], From 5ec15659651bc7aebf4a145894c7b8437c403c7f Mon Sep 17 00:00:00 2001 From: chaign_c Date: Fri, 1 Jun 2018 01:52:59 +0200 Subject: [PATCH 12/34] --add-qemu-options --- Changelog.txt | 9 ++++++++- arm_now/arm_now.py | 15 ++++++++------- arm_now/utils.py | 2 +- 3 files changed, 17 insertions(+), 9 deletions(-) diff --git a/Changelog.txt b/Changelog.txt index d12efb2..ee92aba 100644 --- a/Changelog.txt +++ b/Changelog.txt @@ -32,10 +32,17 @@ Migrate from clize to docopt Refactor utils functions Disable audio to gain boot speed Add colors +Add -add-qemu-options TODO: -Add a project page describing every options, --redir .. +find binary-samples/ -type f |tee filelist | e2cp -apv -d arm_now/rootfs.ext2:/ +10.0.2.2 is the host ip +use mkisofs or genisoimage to create an iso from a folder. +https://unix.stackexchange.com/questions/90793/create-iso-image-from-folder-via-terminal-commands +sudo mount -o loop output_image.iso iso/ + +Add a wiki page describing every options, --redir .. and use of alias Unit test For --offline option check if local filesystem are avaiable otherwise exit cleanly and print a error message. For --resize option check with a regex if the parameter is correct to avoid command/parameter injection. diff --git a/arm_now/arm_now.py b/arm_now/arm_now.py index 51b3f5d..0ec56aa 100755 --- a/arm_now/arm_now.py +++ b/arm_now/arm_now.py @@ -5,7 +5,7 @@ """arm_now. Usage: arm_now list [--all] - arm_now start [] [--clean] [--sync] [--offline] [--redir=]... [--add-qemu-options=] + arm_now start [] [--clean] [--sync] [--offline] [--add-qemu-options=] [--redir=]... arm_now clean arm_now resize [--correct] arm_now install [] [--clean] @@ -23,7 +23,8 @@ --sync Synchronize the current directory with the vm home. --redir protocol:host::guest Redirect the host port to the guest (example: --redir tcp:8000::80) --clean Clean the current image before starting. - --add-qemu-options= Add options to qemu-system-. (example: --add-qemu-options="-no-reboot") + --add-qemu-options= Add options to qemu-system-. + (example: --add-qemu-options="-sandbox on" to Enable seccomp mode 2 system call filter ) --offline Start with zero internet request. --correct Correct the filesystem. -h --help Show this screen. @@ -42,7 +43,7 @@ def main(): elif a["start"]: do_start(a[""] or "armv5-eabi", a["--clean"], a["--sync"], a["--offline"], a["--redir"], - a["--add-qemu-options"]) + ' '.join(a["--add-qemu-options"])) elif a["clean"]: do_clean() elif a["resize"]: @@ -179,12 +180,12 @@ def scrawl_kernel(arch): kernel = None if "kernel" not in links_dict[state][libc] else links_dict[state][libc]["kernel"] return kernel, dtb, rootfs -def run(arch, kernel, dtb, rootfs, redir): +def run(arch, kernel, dtb, rootfs, add_qemu_options): dtb = "" if not os.path.exists(dtb) else "-dtb {}".format(dtb) options = qemu_options[arch][1].format(arch=arch, kernel=kernel, rootfs=rootfs, dtb=dtb) arch = qemu_options[arch][0] print("Starting qemu-system-{}".format(arch)) - qemu_config = "-serial stdio -monitor /dev/null {redir}".format(redir=redir) + qemu_config = "-serial stdio -monitor /dev/null {add_qemu_options}".format(add_qemu_options=add_qemu_options) cmd = """stty intr ^] export QEMU_AUDIO_DRV="none" qemu-system-{arch} {options} \ @@ -314,9 +315,9 @@ def do_start(arch, clean, sync, offline, redir, add_qemu_options): :param sync: Sync le current directory with the guest. """ redir = parse_redir(redir) + add_qemu_options += " " + redir print("o" * 40) print(add_qemu_options) - print(redir) if not arch: print("Supported architectures:") print(list_arch()) @@ -330,7 +331,7 @@ def do_start(arch, clean, sync, offline, redir, add_qemu_options): config_filesystem(ROOTFS, arch) if sync: add_local_files(ROOTFS, "/root") - run(arch, KERNEL, DTB, ROOTFS, redir) + run(arch, KERNEL, DTB, ROOTFS, add_qemu_options) try: print(" Checking the filesystem ".center(80, "+")) subprocess.check_call(["e2fsck", "-vfy", ROOTFS]) diff --git a/arm_now/utils.py b/arm_now/utils.py index fdc12af..720393e 100644 --- a/arm_now/utils.py +++ b/arm_now/utils.py @@ -104,7 +104,7 @@ def add_local_files(rootfs, dest): with tempfile.TemporaryDirectory() as tmpdirname: files = [ i for i in os.listdir(".") if i != "arm_now" and not i.startswith("-") ] if files: - tar = tmpdirname + "/current_directory.tar" + oar = tmpdirname + "/current_directory.tar" subprocess.check_call(["tar", "cf", tar] + files) subprocess.check_call("e2cp -G 0 -O 0".split(' ') + [tar, rootfs + ":/"]) ext2_write_to_file(rootfs, "/etc/init.d/S95_sync_current_diretory",""" From 818259e218d2a60a77f5a58ad83842c2639220ed Mon Sep 17 00:00:00 2001 From: chaign_c Date: Sat, 2 Jun 2018 13:00:59 +0200 Subject: [PATCH 13/34] --autostart=