add needrestart support

This commit is contained in:
JerryXiao 2021-01-16 11:21:05 +08:00
parent fc23239e30
commit 58b8818218
Signed by: Jerry
GPG key ID: 9D9CE43650FF2BAA
5 changed files with 82 additions and 12 deletions

View file

@ -145,7 +145,7 @@ def _stdout_parser(stdout: List[str], report: checkReport) -> None:
if _m := REGEX['s_upgrade_pkg'].match(line):
logger.debug(f's_upgrade_pkg {line=}')
else:
report.crit(f'{line=} is unknown')
logger.debug(f'stdout {line=} is unknown')
else:
logger.debug(f'skip {line=}')
ln += 1

View file

@ -1,4 +1,5 @@
{
"timeout": 300,
"upgrade_timeout": 3600,
"network_retry": 5,
"custom_sync": false,
@ -12,5 +13,8 @@
"ignored_pacnew": [
"/etc/locale.gen",
"/etc/pacman.d/mirrorlist"
]
],
"need_restart": false,
"need_restart_cmd": ["needrestart", "-r", "a", "-m", "a", "-l"],
"systemd-check": true
}

View file

@ -14,9 +14,10 @@ if (cfg := (CONFIG_DIR / CONFIG_FILE)).exists():
else:
_config = dict()
TIMEOUT = int(_config.get('timeout', 300))
UPGRADE_TIMEOUT = int(_config.get('upgrade_timeout', 3600))
NETWORK_RETRY = int(_config.get('network_retry', 5))
assert UPGRADE_TIMEOUT > 0 and NETWORK_RETRY > 0
assert TIMEOUT > 0 and UPGRADE_TIMEOUT > 0 and NETWORK_RETRY > 0
CUSTOM_SYNC = bool(_config.get('custom_sync', False))
SYNC_SH = CONFIG_DIR / str(_config.get('sync_shell', "sync.sh"))
@ -31,4 +32,12 @@ for (k, v) in HOLD.items():
assert isinstance(k, str) and isinstance(v, str)
IGNORED_PACNEW = _config.get('ignored_pacnew', list())
assert isinstance(IGNORED_PACNEW, list)
for i in IGNORED_PACNEW:
assert isinstance(i, str)
NEEDRESTART = bool(_config.get('need_restart', False))
NEEDRESTART_CMD = _config.get('need_restart_cmd', False)
for i in NEEDRESTART_CMD:
assert isinstance(i, str)
SYSTEMD = bool(_config.get('systemd-check', True))

View file

@ -13,7 +13,8 @@ from typing import List, Iterator
from pacroller.utils import execute_with_io, UnknownQuestionError, back_readline
from pacroller.checker import log_checker, sync_err_is_net, upgrade_err_is_net, checkReport
from pacroller.config import (CONFIG_DIR, CONFIG_FILE, LIB_DIR, DB_FILE, PACMAN_LOG, PACMAN_CONFIG,
UPGRADE_TIMEOUT, NETWORK_RETRY, CUSTOM_SYNC, SYNC_SH, EXTRA_SAFE, SHELL, HOLD)
TIMEOUT, UPGRADE_TIMEOUT, NETWORK_RETRY, CUSTOM_SYNC, SYNC_SH,
EXTRA_SAFE, SHELL, HOLD, NEEDRESTART, NEEDRESTART_CMD, SYSTEMD)
logger = logging.getLogger()
@ -29,6 +30,10 @@ class PackageHold(Exception):
pass
class CheckFailed(Exception):
pass
class NeedrestartFailed(Exception):
pass
class SystemdNotRunning(Exception):
pass
def sync() -> None:
logger.info('sync start')
@ -43,7 +48,7 @@ def sync() -> None:
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding='utf-8',
timeout=300
timeout=TIMEOUT
)
except subprocess.CalledProcessError as e:
if sync_err_is_net(e.output):
@ -99,7 +104,7 @@ def upgrade() -> List[str]:
examine_upgrade(t.to_add, t.to_remove)
finally:
t.release()
pacman_output = execute_with_io(['pacman', '-Su', '--noprogressbar', '--color', 'never'])
pacman_output = execute_with_io(['pacman', '-Su', '--noprogressbar', '--color', 'never'], UPGRADE_TIMEOUT)
logger.info('upgrade end')
return pacman_output
@ -132,7 +137,13 @@ def do_system_upgrade(debug=False) -> checkReport:
with open(PACMAN_LOG, 'r') as pacman_log:
pacman_log.seek(log_anchor)
log = pacman_log.read().split('\n')
report = log_checker(stdout, log, debug=debug)
try:
report = log_checker(stdout, log, debug=debug)
except Exception:
logger.exception('checker has crashed, here is the debug info')
_report = log_checker(stdout, log, debug=True)
raise
logger.info(report.summary(verbose=True, show_package=False))
return report
@ -156,6 +167,21 @@ def has_previous_error() -> Exception:
else:
return None
def is_system_failed() -> str:
try:
p = subprocess.run(["systemctl", "is-system-running"],
stdout=subprocess.PIPE, stderr=subprocess.DEVNULL,
stdin=subprocess.DEVNULL,
timeout=20, encoding='utf-8')
except Exception:
ret = "exec fail"
else:
ret = p.stdout.strip()
if ret == 'running':
return None
else:
return ret
def main() -> None:
import argparse
parser = argparse.ArgumentParser(description='Pacman Automatic Rolling Helper')
@ -171,7 +197,12 @@ def main() -> None:
if args.action == 'run':
if getuid() != 0:
parser.error('you need to be root')
logger.error('you need to be root')
exit(1)
if SYSTEMD:
if _s := is_system_failed():
logger.error(f'systemd is not in {_s} state, refused')
exit(11)
if prev_err := has_previous_error():
logger.error(f'Cannot continue, a previous error {prev_err} is still present. Please resolve this issue and run fail-reset.')
else:
@ -190,7 +221,28 @@ def main() -> None:
else:
if report._warn or report._crit:
exc = CheckFailed('manual inspection required')
if exc:
write_db(report, exc)
exit(2)
if NEEDRESTART:
try:
p = subprocess.run(
NEEDRESTART_CMD,
stdin=subprocess.DEVNULL,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
encoding='utf-8',
timeout=TIMEOUT
)
except subprocess.CalledProcessError as e:
logger.error(f'needrestart failed with {e.returncode=} {e.output=}')
exc = NeedrestartFailed(f'{e.returncode=}')
else:
logger.debug(f'needrestart {p.stdout=}')
write_db(report, exc)
if exc:
exit(2)
elif args.action == 'status':
count = 0
for entry in read_db():
@ -202,8 +254,13 @@ def main() -> None:
break
print()
elif args.action == 'fail-reset':
if SYSTEMD:
if _s := is_system_failed():
logger.error(f'systemd is not in {_s} state, refused')
exit(11)
if getuid() != 0:
parser.error('you need to be root')
logger.error('you need to be root')
exit(1)
if prev_err := has_previous_error():
write_db(None)
logger.info(f'reset previous error {prev_err}')

View file

@ -14,7 +14,7 @@ class UnknownQuestionError(subprocess.SubprocessError):
def __str__(self):
return f"Pacman returned an unknown question {self.question}"
def execute_with_io(command: List[str]) -> List[str]:
def execute_with_io(command: List[str], timeout: int = 3600) -> List[str]:
'''
captures stdout and stderr and
automatically handles [y/n] questions of pacman
@ -41,7 +41,7 @@ def execute_with_io(command: List[str]) -> List[str]:
stderr=subprocess.STDOUT,
encoding='utf-8'
)
Thread(target=set_timeout, args=(p, 3600), daemon=True).start() # should be configurable
Thread(target=set_timeout, args=(p, timeout), daemon=True).start() # should be configurable
line = ''
output = ''
while (r := p.stdout.read(1)) != '':