Merge pull request #267 from postmarketOS/fix/242-validate-input

Fix #242: Validate input in pmb.helpers.cli
This commit is contained in:
Martijn Braam 2017-07-27 20:08:26 +02:00 committed by GitHub
commit 3be2fce72f
5 changed files with 91 additions and 42 deletions

View File

@ -48,8 +48,8 @@ def extract(args, flavor, suffix, log_message=False):
inside = "/tmp/initfs-extracted"
outside = args.work + "/chroot_" + suffix + inside
if os.path.exists(outside):
if pmb.helpers.cli.ask(args, "Extraction folder " + outside +
" already exists. Do you want to overwrite it?") != "y":
if not pmb.helpers.cli.confirm(args, "Extraction folder " + outside +
" already exists. Do you want to overwrite it?"):
raise RuntimeError("Aborted!")
pmb.chroot.root(args, ["rm", "-r", inside], suffix)

View File

@ -42,5 +42,5 @@ def zap(args):
pattern = os.path.abspath(args.work + "/" + pattern)
matches = glob.glob(pattern)
for match in matches:
if pmb.helpers.cli.ask(args, "Remove " + match + "?") == "y":
if pmb.helpers.cli.confirm(args, "Remove " + match + "?"):
pmb.helpers.run.root(args, ["rm", "-rf", match])

View File

@ -26,6 +26,38 @@ import pmb.helpers.devices
import pmb.helpers.ui
def ask_for_work_path(args):
"""
Ask for the work path, until we can create it (when it does not exist) and
write into it.
:returns: the work path
"""
logging.info("Location of the 'work' path. Multiple chroots"
" (native, device arch, device rootfs) will be created"
" in there.")
while True:
try:
ret = os.path.expanduser(pmb.helpers.cli.ask(
args, "Work path", None, args.work, False))
os.makedirs(ret + "/chroot_native", 0o700, True)
return ret
except OSError:
logging.fatal("ERROR: Could not create this folder, or write"
" inside it! Please try again.")
def ask_for_ui(args):
ui_list = pmb.helpers.ui.list(args)
logging.info("Available user interfaces (" +
str(len(ui_list) - 1) + "): " + ", ".join(ui_list))
while True:
ret = pmb.helpers.cli.ask(args, "User interface", None, args.ui, True)
if ret in ui_list:
return ret
logging.fatal("ERROR: Invalid user interface specified, please type in"
" one from the list above.")
def init(args):
cfg = pmb.config.load(args)
@ -36,20 +68,11 @@ def init(args):
logging.info("Available (" + str(len(devices)) + "): " +
", ".join(devices))
cfg["pmbootstrap"]["device"] = pmb.helpers.cli.ask(args, "Device",
None, args.device)
None, args.device, False, "[a-z0-9]+-[a-z0-9]+")
# UI selection
ui_list = pmb.helpers.ui.list(args)
logging.info("Available user interfaces (" + str(len(ui_list) - 1) + "): " + ", ".join(ui_list))
cfg["pmbootstrap"]["ui"] = pmb.helpers.cli.ask(args, "User Interface:",
None, args.ui, True)
# Work folder
logging.info("Location of the 'work' path. Multiple chroots (native,"
" device arch, device rootfs) will be created in there.")
cfg["pmbootstrap"]["work"] = os.path.expanduser(pmb.helpers.cli.ask(args, "Work path",
None, args.work, False))
os.makedirs(cfg["pmbootstrap"]["work"], 0o700, True)
# UI and work folder
cfg["pmbootstrap"]["ui"] = ask_for_ui(args)
cfg["pmbootstrap"]["work"] = ask_for_work_path(args)
# Parallel job count
default = args.jobs
@ -58,17 +81,15 @@ def init(args):
logging.info("How many jobs should run parallel on this machine, when"
" compiling?")
cfg["pmbootstrap"]["jobs"] = pmb.helpers.cli.ask(args, "Jobs",
None, default)
None, default, validation_regex="[1-9][0-9]*")
# Timestamp based rebuilds
default = "y"
if not args.timestamp_based_rebuild:
default = "n"
logging.info("Rebuild packages, when the last modified timestamp changed,"
" even if the version did not change? This makes pmbootstrap"
" behave more like 'make'.")
answer = pmb.helpers.cli.ask(args, "Timestamp based rebuilds",
default=default)
cfg["pmbootstrap"]["timestamp_based_rebuild"] = str(answer == "y")
answer = pmb.helpers.cli.confirm(args, "Timestamp based rebuilds",
default=args.timestamp_based_rebuild)
cfg["pmbootstrap"]["timestamp_based_rebuild"] = str(answer)
# Do not save aports location to config file
del cfg["pmbootstrap"]["aports"]

View File

@ -17,23 +17,51 @@ You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import datetime
import logging
import re
def ask(args, question="Continue?", choices=['y', 'n'], default='n',
lowercase_answer=True):
date = datetime.datetime.now().strftime("%H:%M:%S")
question = "[" + date + "] " + question
if choices:
question += " (" + str.join("/", choices) + ")"
if default:
question += " [" + str(default) + "]"
def ask(args, question="Continue?", choices=["y", "n"], default="n",
lowercase_answer=True, validation_regex=None):
"""
Ask a question on the terminal. When validation_regex is set, the user gets
asked until the answer matches the regex.
:returns: the user's answer
"""
while True:
date = datetime.datetime.now().strftime("%H:%M:%S")
question_full = "[" + date + "] " + question
if choices:
question_full += " (" + str.join("/", choices) + ")"
if default:
question_full += " [" + str(default) + "]"
ret = input(question + ": ")
if lowercase_answer:
ret = ret.lower()
if ret == "":
ret = str(default)
ret = input(question_full + ": ")
if lowercase_answer:
ret = ret.lower()
if ret == "":
ret = str(default)
args.logfd.write(question + " " + ret + "\n")
args.logfd.flush()
return ret
args.logfd.write(question_full + " " + ret + "\n")
args.logfd.flush()
# Validate with regex
if not validation_regex:
return ret
pattern = re.compile(validation_regex)
if pattern.match(ret):
return ret
logging.fatal("ERROR: Input did not pass validation (regex: " +
validation_regex + "). Please try again.")
def confirm(args, question="Continue?", default=False):
"""
Convenience wrapper around ask for simple yes-no questions with validation.
:returns: True for "y", False for "n"
"""
default_str = "y" if default else "n"
answer = ask(args, question, ["y", "n"], default_str, True, "(y|n)")
return answer == "y"

View File

@ -37,8 +37,8 @@ def mount_sdcard(args):
if pmb.helpers.mount.ismount(path):
raise RuntimeError(path + " is mounted! We will not attempt"
" to format this!")
if pmb.helpers.cli.ask(args, "EVERYTHING ON " + args.sdcard + " WILL BE"
" ERASED! CONTINUE?") != "y":
if not pmb.helpers.cli.confirm(args, "EVERYTHING ON " + args.sdcard +
" WILL BE ERASED! CONTINUE?"):
raise RuntimeError("Aborted.")
logging.info("(native) mount /dev/install (host: " + args.sdcard + ")")
@ -66,7 +66,7 @@ def create_and_mount_image(args):
logging.info("(native) create " + args.device + ".img (" + size + ")")
logging.info("WARNING: Make sure, that your target device's partition"
" table has allocated at least " + size + " as system partition!")
if pmb.helpers.cli.ask(args) != "y":
if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.")
pmb.chroot.user(args, ["mkdir", "-p", "/home/user/rootfs"])