Those hooks can be used by tools that manage /etc to get notified
about updated configuration files. For example, etckeeper could hook
this mechanism like the following:

/etc/portage/conf-update.d/etckeeper
case "${1}" in
    pre-update)
        etckeeper pre-install
    ;;
    post-update)
                etckeeper post-install
    ;;
esac

Signed-off-by: Florian Schmaus <f...@geekplace.eu>
Closes: https://bugs.gentoo.org/698316
---
 bin/dispatch-conf            |  8 +++++++-
 lib/portage/dispatch_conf.py | 25 ++++++++++++++++++++++++-
 2 files changed, 31 insertions(+), 2 deletions(-)

diff --git a/bin/dispatch-conf b/bin/dispatch-conf
index 1759b89b8..ff9698f14 100755
--- a/bin/dispatch-conf
+++ b/bin/dispatch-conf
@@ -29,7 +29,7 @@ import portage
 portage._internal_caller = True
 from portage import os, shutil
 from portage import _encodings, _unicode_decode
-from portage.dispatch_conf import diffstatusoutput, diff_mixed_wrapper
+from portage.dispatch_conf import diffstatusoutput, diff_mixed_wrapper, 
perform_conf_update_hooks
 from portage.process import find_binary, spawn
 from portage.util import writemsg, writemsg_stdout
 
@@ -382,11 +382,17 @@ class dispatch:
                 encoding=_encodings["stdio"]) as f:
                 f.write(output + "\n")
 
+        perform_conf_update_hooks("pre-update", curconf)
+
         try:
             os.rename(newconf, curconf)
         except (IOError, os.error) as why:
             writemsg('dispatch-conf: Error renaming %s to %s: %s; fatal\n' % \
                   (newconf, curconf, str(why)), noiselevel=-1)
+            return
+
+        perform_conf_update_hooks("post-update", curconf)
+
 
 
     def post_process(self, curconf):
diff --git a/lib/portage/dispatch_conf.py b/lib/portage/dispatch_conf.py
index 71693bb36..8f40c7724 100644
--- a/lib/portage/dispatch_conf.py
+++ b/lib/portage/dispatch_conf.py
@@ -16,11 +16,13 @@ import subprocess
 import sys
 import tempfile
 
+from collections import OrderedDict
+
 import portage
 from portage import _encodings, os, shutil
 from portage.env.loaders import KeyValuePairFileLoader
 from portage.localization import _
-from portage.util import shlex_split, varexpand
+from portage.util import shlex_split, varexpand, _recursive_file_list
 from portage.util.path import iter_parents
 
 RCS_BRANCH = '1.1.1'
@@ -386,3 +388,24 @@ def file_archive_post_process(archive):
                if os.path.isdir(dest) and not os.path.islink(dest):
                        _file_archive_rotate(dest)
                os.rename(archive + '.dist.new', dest)
+
+def perform_conf_update_hooks(kind, conf):
+       conf_update_dir = os.path.join(portage.settings["PORTAGE_CONFIGROOT"],
+               portage.USER_CONFIG_PATH, "conf-update.d")
+       hooks = OrderedDict()
+       for filepath in _recursive_file_list(conf_update_dir):
+               name = filepath.split(conf_update_dir)[1].lstrip(os.sep)
+               if os.access(filepath, os.X_OK):
+                       hooks[filepath] = name
+               else:
+                       writemsg_level(" %s %s hook: '%s' is not executable\n"
+                                      % (warn("*"), _dir, 
_unicode_decode(name),),
+                                               level=logging.WARN, 
noiselevel=2)
+
+       for filepath in hooks:
+               retval = portage.process.spawn([filepath, kind, conf])
+
+               if retval != os.EX_OK:
+                       writemsg_level(" %s Spawn failed for: %s, %s\n" % 
(bad("*"),
+                                       _unicode_decode(_hooks[filepath]), 
filepath),
+                                       level=logging.ERROR, noiselevel=-1)
-- 
2.26.2


Reply via email to