Make all _run() use a list instead of a string.

Add redirect operator (>) to list of not quoted chars.

BUG=chromium:1081031
TEST=OOBE and Backoff

Change-Id: I217a8919e67f0d04203bf8930181e3425a3715c6
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/autotest/+/2223752
Commit-Queue: David Haddock <dhaddock@chromium.org>
Tested-by: David Haddock <dhaddock@chromium.org>
Reviewed-by: Kyle Shimabukuro <kyleshima@chromium.org>
diff --git a/client/common_lib/utils.py b/client/common_lib/utils.py
index 0787097..d90b4eb 100644
--- a/client/common_lib/utils.py
+++ b/client/common_lib/utils.py
@@ -95,7 +95,7 @@
 # safe characters for the shell (do not need quoting)
 SHELL_QUOTING_WHITELIST = frozenset(string.ascii_letters +
                                     string.digits +
-                                    '_-+=')
+                                    '_-+=>')
 
 def custom_warning_handler(message, category, filename, lineno, file=None,
                            line=None):
diff --git a/client/cros/update_engine/update_engine_test.py b/client/cros/update_engine/update_engine_test.py
index 1dfdfc7..393902d 100644
--- a/client/cros/update_engine/update_engine_test.py
+++ b/client/cros/update_engine/update_engine_test.py
@@ -43,13 +43,13 @@
             return
 
         self._internet_was_disabled = False
-        logging.debug('Before reconnect: %s', utils.run('ifconfig'))
+        logging.debug('Before reconnect: %s', utils.run(['ifconfig']))
         for eth in self._NETWORK_INTERFACES:
-            utils.run('ifconfig %s up' % eth, ignore_status=True)
+            utils.run(['ifconfig', eth, 'up'], ignore_status=True)
         utils.start_service('recover_duts', ignore_status=True)
 
         # Print ifconfig to help debug DUTs that stay offline.
-        logging.debug('After reconnect: %s', utils.run('ifconfig'))
+        logging.debug('After reconnect: %s', utils.run(['ifconfig']))
 
         # We can't return right after reconnecting the network or the server
         # test may not receive the message. So we wait a bit longer for the
@@ -66,14 +66,15 @@
         """Disable the internet connection"""
         self._internet_was_disabled = True
         try:
-            logging.debug('Before disconnect: %s', utils.run('ifconfig'))
+            logging.debug('Before disconnect: %s', utils.run(['ifconfig']))
             # DUTs in the lab have a service called recover_duts that is used to
             # check that the DUT is online and if it is not it will bring it
             # back online. We will need to stop this service for the length
             # of this test.
             utils.stop_service('recover_duts', ignore_status=True)
             for eth in self._NETWORK_INTERFACES:
-                result = utils.run('ifconfig %s down' % eth, ignore_status=True)
+                result = utils.run(['ifconfig', eth, 'down'],
+                                   ignore_status=True)
                 logging.debug(result)
 
             # Print ifconfig to help debug DUTs that stay online.
@@ -88,7 +89,7 @@
                                      desc='Ping failure while offline.')
         except (error.CmdError, utils.TimeoutError):
             logging.exception('Failed to disconnect one or more interfaces.')
-            logging.debug(utils.run('ifconfig', ignore_status=True))
+            logging.debug(utils.run(['ifconfig'], ignore_status=True))
             raise error.TestFail('Disabling the internet connection failed.')
 
 
@@ -161,4 +162,4 @@
                                      'Before: %f, After: %f' % (progress_before,
                                                                 progress_after))
 
-        self._enable_internet()
\ No newline at end of file
+        self._enable_internet()
diff --git a/client/cros/update_engine/update_engine_util.py b/client/cros/update_engine/update_engine_util.py
index ab235c3..e86a128 100644
--- a/client/cros/update_engine/update_engine_util.py
+++ b/client/cros/update_engine/update_engine_util.py
@@ -243,8 +243,7 @@
             entry = (entry,)
 
         if not update_engine_log:
-            update_engine_log = self._run(
-                'cat %s' % self._UPDATE_ENGINE_LOG).stdout
+            update_engine_log = self._get_update_engine_log()
 
         if all(msg in update_engine_log for msg in entry):
             return True
@@ -522,8 +521,8 @@
 
         """
         files = self._get_update_engine_logs()
-        return self._run('cat %s' % os.path.join(self._UPDATE_ENGINE_LOG_DIR,
-                                                 files[r_index])).stdout
+        log_file = os.path.join(self._UPDATE_ENGINE_LOG_DIR, files[r_index])
+        return self._run(['cat', log_file]).stdout
 
 
     def _create_custom_lsb_release(self, update_url, build='0.0.0.0', **kwargs):
@@ -543,13 +542,13 @@
         """
         update_url = self._append_query_to_url(update_url, kwargs)
 
-        self._run('mkdir %s' % os.path.dirname(self._CUSTOM_LSB_RELEASE),
+        self._run(['mkdir', os.path.dirname(self._CUSTOM_LSB_RELEASE)],
                   ignore_status=True)
-        self._run('touch %s' % self._CUSTOM_LSB_RELEASE)
-        self._run('echo "CHROMEOS_RELEASE_VERSION=%s" > %s' %
-                  (build, self._CUSTOM_LSB_RELEASE))
-        self._run('echo "CHROMEOS_AUSERVER=%s" >> %s' %
-                  (update_url, self._CUSTOM_LSB_RELEASE))
+        self._run(['touch', self._CUSTOM_LSB_RELEASE])
+        self._run(['echo', 'CHROMEOS_RELEASE_VERSION=%s' % build, '>',
+                   self._CUSTOM_LSB_RELEASE])
+        self._run(['echo', 'CHROMEOS_AUSERVER=%s' % update_url, '>>',
+                   self._CUSTOM_LSB_RELEASE])
 
 
     def _clear_custom_lsb_release(self):
@@ -559,7 +558,7 @@
         Intended to clear work done by _create_custom_lsb_release().
 
         """
-        self._run('rm %s' % self._CUSTOM_LSB_RELEASE, ignore_status=True)
+        self._run(['rm', self._CUSTOM_LSB_RELEASE], ignore_status=True)
 
 
     def _get_update_requests(self):
@@ -613,7 +612,7 @@
         """
         try:
             file_location = os.path.join('/tmp', filename)
-            self._run('screenshot %s' % file_location)
+            self._run(['screenshot', file_location])
             self._get_file(file_location, self.resultsdir)
         except (error.AutoservRunError, error.CmdError):
             logging.exception('Failed to take screenshot.')
diff --git a/server/cros/update_engine/update_engine_test.py b/server/cros/update_engine/update_engine_test.py
index a41416f..9263fdd 100644
--- a/server/cros/update_engine/update_engine_test.py
+++ b/server/cros/update_engine/update_engine_test.py
@@ -343,9 +343,10 @@
 
         """
         payload_filename = payload_url.rpartition('/')[2]
-        utils.run('gsutil cp %s* %s' % (payload_url, self._CELLULAR_BUCKET))
+        utils.run(['gsutil', 'cp', '%s*' % payload_url, self._CELLULAR_BUCKET])
         new_gs_url = self._CELLULAR_BUCKET + payload_filename
-        utils.run('gsutil acl ch -u AllUsers:R %s*' % new_gs_url)
+        utils.run(['gsutil', 'acl', 'ch', '-u', 'AllUsers:R',
+                   '%s*' % new_gs_url])
         return new_gs_url.replace('gs://', 'https://storage.googleapis.com/')