Blob Blame History Raw
From 48a99c95078bab306cb56bb1a05420d88bf15a64 Mon Sep 17 00:00:00 2001
From: "Bryn M. Reeves" <bmr@redhat.com>
Date: Mon, 20 Oct 2014 16:12:01 +0100
Subject: [PATCH] [plugin] limit names to PC_NAME_MAX

Commit 8bf7b0c removed the truncation of mangled command names to
64 chars. This causes problems for some plugins (e.g. Issue #415)
that generate long enough command lines to hit system name length
limits.

Instead of arbitrarily limiting to 64 characters limit to the
lesser of the archive format limit (if present) or the value of
PC_NAME_MAX for any intermediate FileCacheArchive path.

Signed-off-by: Bryn M. Reeves <bmr@redhat.com>
---
 sos/archive.py          | 18 ++++++++++++++++++
 sos/plugins/__init__.py |  7 ++++---
 tests/plugin_tests.py   | 12 ++++++++----
 3 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/sos/archive.py b/sos/archive.py
index 2e42bbd..6063625 100644
--- a/sos/archive.py
+++ b/sos/archive.py
@@ -98,6 +98,12 @@ class Archive(object):
         to be included in the generated archive."""
         raise NotImplementedError
 
+    def name_max(self):
+        """Return the maximum file name length this archive can support.
+        This is the lesser of the name length limit of the archive
+        format and any temporary file system based cache."""
+        raise NotImplementedError
+
     def get_archive_path(self):
         """Return a string representing the path to the temporary
         archive. For archive classes that implement in-line handling
@@ -206,6 +212,13 @@ class FileCacheArchive(Archive):
     def _makedirs(self, path, mode=0o700):
         os.makedirs(path, mode)
 
+    def name_max(self):
+        if 'PC_NAME_MAX' in os.pathconf_names:
+            pc_name_max = os.pathconf_names['PC_NAME_MAX']
+            return os.pathconf(self._archive_root, pc_name_max)
+        else:
+            return 255
+
     def get_tmp_dir(self):
         return self._archive_root
 
@@ -355,6 +368,11 @@ class TarFileArchive(FileCacheArchive):
     def name(self):
         return "%s.%s" % (self._name, self._suffix)
 
+    def name_max(self):
+        # GNU Tar format supports unlimited file name length. Just return
+        # the limit of the underlying FileCacheArchive.
+        return super(TarFileArchive, self).name_max()
+
     def _build_archive(self):
         # python2.6 TarFile lacks the filter parameter
         if not six.PY3 and sys.version_info[1] < 7:
diff --git a/sos/plugins/__init__.py b/sos/plugins/__init__.py
index 3e39100..b289144 100644
--- a/sos/plugins/__init__.py
+++ b/sos/plugins/__init__.py
@@ -42,11 +42,11 @@ def regex_findall(regex, fname):
         return []
 
 
-def _mangle_command(command):
-    # FIXME: this can be improved
+def _mangle_command(command, name_max):
     mangledname = re.sub(r"^/(usr/|)(bin|sbin)/", "", command)
     mangledname = re.sub(r"[^\w\-\.\/]+", "_", mangledname)
     mangledname = re.sub(r"/", ".", mangledname).strip(" ._-")
+    mangledname = mangledname[0:name_max]
     return mangledname
 
 
@@ -518,7 +518,8 @@ class Plugin(object):
         return grep(regexp, *fnames)
 
     def _mangle_command(self, exe):
-        return _mangle_command(exe)
+        name_max = self.archive.name_max()
+        return _mangle_command(exe, name_max)
 
     def _make_command_filename(self, exe):
         """The internal function to build up a filename based on a command."""
diff --git a/tests/plugin_tests.py b/tests/plugin_tests.py
index 817e4f2..f73a003 100644
--- a/tests/plugin_tests.py
+++ b/tests/plugin_tests.py
@@ -115,10 +115,14 @@ class PluginToolTests(unittest.TestCase):
         self.assertEquals(matches, [])
 
     def test_mangle_command(self):
-        self.assertEquals("foo", _mangle_command("/usr/bin/foo"))
-        self.assertEquals("foo_-x", _mangle_command("/usr/bin/foo -x"))
-        self.assertEquals("foo_--verbose", _mangle_command("/usr/bin/foo --verbose"))
-        self.assertEquals("foo_.path.to.stuff", _mangle_command("/usr/bin/foo /path/to/stuff"))
+        name_max = 255
+        self.assertEquals("foo", _mangle_command("/usr/bin/foo", name_max))
+        self.assertEquals("foo_-x", _mangle_command("/usr/bin/foo -x", name_max))
+        self.assertEquals("foo_--verbose", _mangle_command("/usr/bin/foo --verbose", name_max))
+        self.assertEquals("foo_.path.to.stuff", _mangle_command("/usr/bin/foo /path/to/stuff", name_max))
+        longcmd ="foo is " + "a" * 256 + " long_command"
+        expected = longcmd[0:name_max].replace(' ', '_')
+        self.assertEquals(expected, _mangle_command(longcmd, name_max))
 
 
 class PluginTests(unittest.TestCase):
-- 
1.9.3