From 27fb0453a37027406eb2d21bcc81271144bbdac0 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Tue, 4 Apr 2023 10:40:34 +0100 Subject: [PATCH] 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 eaccd74..4fb385f 100644 --- a/devlib/target.py +++ b/devlib/target.py @@ -974,8 +974,19 @@ class Target(object): 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 @@ class LinuxTarget(Target): 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 @@ class AndroidTarget(Target): 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