From 1f7ebd987e638911f66a7be4f612d445eb687726 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Tue, 4 Apr 2023 10:39:25 +0100 Subject: [PATCH 1/2] shutils: Fix broken redirections Redirecting all output to /dev/null needs >/dev/null 2>&1 . Fix cases where 2>&1 /dev/null was used, and also remove &> that is not POSIX. --- devlib/bin/scripts/shutils.in | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/devlib/bin/scripts/shutils.in b/devlib/bin/scripts/shutils.in index 458c97f00..6607504e9 100755 --- a/devlib/bin/scripts/shutils.in +++ b/devlib/bin/scripts/shutils.in @@ -161,7 +161,7 @@ cgroups_run_into() { # Check if the required CGroup exists $FIND $CGMOUNT -type d -mindepth 1 | \ - $GREP -E "^$CGMOUNT/devlib_cgh[0-9]{1,2}$CGP" &>/dev/null + $GREP -E "^$CGMOUNT/devlib_cgh[0-9]{1,2}$CGP" >/dev/null 2>&1 if [ $? -ne 0 ]; then echo "ERROR: could not find any $CGP cgroup under $CGMOUNT" exit 1 @@ -345,7 +345,7 @@ _command_not_found() { exit 1 } # Check the command exists -type "$CMD" 2>&1 >/dev/null || _command_not_found +type "$CMD" >/dev/null 2>&1 || _command_not_found "$CMD" "$@" From 6877f0338a349cdd6090b60d8d828c300fd30435 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Tue, 4 Apr 2023 10:40:34 +0100 Subject: [PATCH 2/2] target: Fix and generalize Target.kick_off() kick_off() implementation had a number of issue: * Target specific implementation making testing more difficult. * Not wrapping the command in sh -c lead to blocking behavior if the command had multiple parts, e.g. "sleep 42; echo foo" * nohup sometimes writes to stdout, breaking return code parsing in adb_shell(). These issues are fixed by a generic implementing of kick_off() that simply delegates to Target.background(). Fixes https://github.com/ARM-software/devlib/issues/623 --- devlib/target.py | 35 +++++++++++++---------------------- 1 file changed, 13 insertions(+), 22 deletions(-) diff --git a/devlib/target.py b/devlib/target.py index eaccd747d..4fb385fa3 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -974,8 +974,19 @@ def background_invoke(self, binary, args=None, in_directory=None, command = 'cd {} && {}'.format(quote(in_directory), command) return self.background(command, as_root=as_root) - def kick_off(self, command, as_root=False): - raise NotImplementedError() + @asyn.asyncf + async def kick_off(self, command, as_root=None): + """ + Like execute() but returns immediately. Unlike background(), it will + not return any handle to the command being run. + """ + cmd = 'cd {wd} && {busybox} sh -c {cmd} >/dev/null 2>&1'.format( + wd=quote(self.working_directory), + busybox=quote(self.busybox), + cmd=quote(command) + ) + self.background(cmd, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, as_root=as_root) + # sysfs interaction @@ -1581,11 +1592,6 @@ def __init__(self, def wait_boot_complete(self, timeout=10): pass - @call_conn - def kick_off(self, command, as_root=False): - command = 'sh -c {} 1>/dev/null 2>/dev/null &'.format(quote(command)) - return self.conn.execute(command, as_root=as_root) - @asyn.asyncf async def get_pids_of(self, process_name): """Returns a list of PIDs of all processes with the specified name.""" @@ -1833,21 +1839,6 @@ async def connect(self, timeout=30, check_boot_completed=True, max_async=None): max_async=max_async, ) - @asyn.asyncf - async def kick_off(self, command, as_root=None): - """ - Like execute but closes adb session and returns immediately, leaving the command running on the - device (this is different from execute(background=True) which keeps adb connection open and returns - a subprocess object). - """ - if as_root is None: - as_root = self.needs_su - try: - command = 'cd {} && {} nohup {} &'.format(quote(self.working_directory), quote(self.busybox), command) - await self.execute.asyn(command, timeout=1, as_root=as_root) - except TimeoutError: - pass - @asyn.asyncf async def __setup_list_directory(self): # In at least Linaro Android 16.09 (which was their first Android 7 release) and maybe