pmbootstrap-meow/pmb/helpers/other.py
Oliver Smith 8b8a73e8dd
Fix crash from mixing 'sudo' with 'du' output (!1777)
Do not crash during "pmbootstrap zap" and other actions when the user
needs to type in the sudo password before running "du" to calculate the
work folder size. We're trying to parse the "du" output as integer, but
of course the 'Sorry, try again' message from sudo is not a valid
integer.

$ pmbootstrap --details-to-stdout zap
(008707) [14:00:57] Shutdown complete
(008707) [14:00:57] Calculate work folder size
(008707) [14:00:57] % sudo du --summarize --block-size=1 /home/luca/.local/var/pmbootstrap
[sudo] password for luca:
[sudo] password for luca: Sorry, try again.

12966293504     /home/luca/.local/var/pmbootstrap
(008707) [14:01:03] ERROR: invalid literal for int() with base 10: 'Sorry, try again.\n12966293504'

It would be a good idea to separate the sudo output from the process
output in general, see #1677 for a related proposal.
2019-04-22 00:00:58 +02:00

187 lines
6.5 KiB
Python

"""
Copyright 2019 Oliver Smith
This file is part of pmbootstrap.
pmbootstrap is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pmbootstrap is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pmbootstrap. If not, see <http://www.gnu.org/licenses/>.
"""
import logging
import os
import re
import pmb.chroot
import pmb.config
import pmb.helpers.pmaports
import pmb.helpers.run
def folder_size(args, path):
"""
Run `du` to calculate the size of a folder (this is less code and
faster than doing the same task in pure Python). This result is only
approximatelly right, but good enough for pmbootstrap's use case (#760).
:returns: folder size in bytes
"""
output = pmb.helpers.run.root(args, ["du", "--summarize",
"--block-size=1",
path], output_return=True)
# Only look at last line to filter out sudo garbage (#1766)
last_line = output.split("\n")[-2]
ret = int(last_line.split("\t")[0])
return ret
def check_grsec(args):
"""
Check if the current kernel is based on the grsec patchset, and if
the chroot_deny_chmod option is enabled. Raise an exception in that
case, with a link to the issue. Otherwise, do nothing.
"""
path = "/proc/sys/kernel/grsecurity/chroot_deny_chmod"
if not os.path.exists(path):
return
raise RuntimeError("You're running a kernel based on the grsec"
" patchset. This is not supported.")
def check_binfmt_misc(args):
"""
Check if the 'binfmt_misc' module is loaded by checking, if
/proc/sys/fs/binfmt_misc/ exists. If it exists, then do nothing.
Otherwise, raise an exception pointing to user to the Wiki.
"""
path = "/proc/sys/fs/binfmt_misc/status"
if os.path.exists(path):
return
link = "https://postmarketos.org/binfmt_misc"
raise RuntimeError("It appears that your system has not loaded the"
" module 'binfmt_misc'. This is required to run"
" foreign architecture programs with QEMU (eg."
" armhf on x86_64):\n See: <" + link + ">")
def migrate_success(args, version):
logging.info("Migration to version " + str(version) + " done")
with open(args.work + "/version", "w") as handle:
handle.write(str(version) + "\n")
def migrate_work_folder(args):
# Read current version
current = 0
path = args.work + "/version"
if os.path.exists(path):
with open(path, "r") as f:
current = int(f.read().rstrip())
# Compare version, print warning or do nothing
required = pmb.config.work_version
if current == required:
return
logging.info("WARNING: Your work folder version needs to be migrated"
" (from version " + str(current) + " to " + str(required) +
")!")
# 0 => 1
if current == 0:
# Ask for confirmation
logging.info("Changelog:")
logging.info("* Building chroots have a different username (#709)")
logging.info("Migration will do the following:")
logging.info("* Zap your chroots")
logging.info("* Adjust '" + args.work + "/config_abuild/abuild.conf'")
if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.")
# Zap and update abuild.conf
pmb.chroot.zap(args, False)
conf = args.work + "/config_abuild/abuild.conf"
if os.path.exists(conf):
pmb.helpers.run.root(args, ["sed", "-i",
"s./home/user/./home/pmos/.g", conf])
# Update version file
migrate_success(args, 1)
current = 1
# 1 => 2
if current == 1:
# Ask for confirmation
logging.info("Changelog:")
logging.info("* Fix: cache_distfiles was writable for everyone")
logging.info("Migration will do the following:")
logging.info("* Fix permissions of '" + args.work +
"/cache_distfiles'")
if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.")
# Fix permissions
dir = "/var/cache/distfiles"
for cmd in [["chown", "-R", "root:abuild", dir],
["chmod", "-R", "664", dir],
["chmod", "a+X", dir]]:
pmb.chroot.root(args, cmd)
migrate_success(args, 2)
current = 2
if current == 2:
# Ask for confirmation
logging.info("Changelog:")
logging.info("* Device chroots have a different user UID (#1576)")
logging.info("Migration will do the following:")
logging.info("* Zap your chroots")
if not pmb.helpers.cli.confirm(args):
raise RuntimeError("Aborted.")
# Zap chroots
pmb.chroot.zap(args, False)
# Update version file
migrate_success(args, 3)
current = 3
# Can't migrate, user must delete it
if current != required:
raise RuntimeError("Sorry, we can't migrate that automatically. Please"
" run 'pmbootstrap shutdown', then delete your"
" current work folder manually ('sudo rm -rf " +
args.work + "') and start over with 'pmbootstrap"
" init'. All your binary packages and caches will"
" be lost.")
def validate_hostname(hostname):
"""
Check whether the string is a valid hostname, according to
<http://en.wikipedia.org/wiki/Hostname#Restrictions_on_valid_host_names>
"""
# Check length
if len(hostname) > 63:
logging.fatal("ERROR: Hostname '" + hostname + "' is too long.")
return False
# Check that it only contains valid chars
if not re.match("^[0-9a-z-]*$", hostname):
logging.fatal("ERROR: Hostname must only contain letters (a-z),"
" digits (0-9) or minus signs (-)")
return False
# Check that doesn't begin or end with a minus sign
if hostname[:1] == "-" or hostname[-1:] == "-":
logging.fatal("ERROR: Hostname must not begin or end with a minus sign")
return False
return True