]> git.ipfire.org Git - pakfire.git/commitdiff
Add code, that actually runs scriptlets.
authorMichael Tremer <michael.tremer@ipfire.org>
Wed, 10 Aug 2011 16:13:38 +0000 (18:13 +0200)
committerMichael Tremer <michael.tremer@ipfire.org>
Wed, 10 Aug 2011 16:13:38 +0000 (18:13 +0200)
pakfire/actions.py
pakfire/constants.py
pakfire/errors.py
pakfire/packages/file.py
po/pakfire.pot

index e0bf678908bae0467f1976c72ebe792b4b8771a9..1518ae980eda44e98b736d7724a01d8aeb69d7a9 100644 (file)
 ###############################################################################
 
 import logging
+import os
 
+import chroot
 import packages
+import util
 
 from constants import *
 from i18n import _
@@ -37,12 +40,18 @@ class Action(object):
                if binary_package:
                        self.pkg = binary_package
 
+               self.init()
+
        def __cmp__(self, other):
                return cmp(self.pkg, other.pkg)
 
        def __repr__(self):
                return "<%s %s>" % (self.__class__.__name__, self.pkg.friendly_name)
 
+       def init(self):
+               # A function to run additional initialization.
+               pass
+
        @property
        def needs_download(self):
                return self.type in ("install", "reinstall", "upgrade", "downgrade",) \
@@ -68,33 +77,143 @@ class Action(object):
 class ActionScript(Action):
        type = "script"
 
+       def init(self):
+               # Load the scriplet.
+               self.scriptlet = self.pkg.scriptlet
+
+       @property
+       def interpreter(self):
+               """
+                       Get the interpreter of this scriptlet.
+               """
+               # XXX check, how to handle elf files here.
+
+               # If nothing was found, we return the default interpreter.
+               interpreter = SCRIPTLET_INTERPRETER
+
+               for line in self.scriptlet.splitlines():
+                       if line.startswith("#!/"):
+                               interpreter = line[2:]
+                               interpreter = interpreter.split()[0]
+                       break
+
+               return interpreter
+
+       @property
+       def args(self):
+               raise NotImplementedError
+
        def run(self):
-               #print "Pretending to run script: %s" % self.__class__.__name__
-               pass
+               # Exit immediately, if the scriptlet is empty.
+               if not self.scriptlet:
+                       return
+
+               # Actually run the scriplet.
+               logging.debug("Running scriptlet %s" % self)
+
+               # Check if the interpreter does exist and is executable.
+               interpreter = "%s/%s" % (self.pakfire.path, self.interpreter)
+               if not os.path.exists(interpreter):
+                       raise ActionError, _("Cannot run scriptlet because no interpreter is available: %s" \
+                               % self.interpreter)
+
+               if not os.access(interpreter, os.X_OK):
+                       raise ActionError, _("Cannot run scriptlet because the interpreter is not executable: %s" \
+                               % self.interpreter)
+
+               # Create a name for the temporary script file.
+               script_file_chroot = os.path.join("/", LOCAL_TMP_PATH,
+                       "scriptlet_%s" % util.random_string(10))
+               script_file = os.path.join(self.pakfire.path, script_file_chroot[1:])
+               assert script_file.startswith("%s/" % self.pakfire.path)
+
+               # Create script directory, if it does not exist.
+               script_dir = os.path.dirname(script_file)
+               if not os.path.exists(script_dir):
+                       os.makedirs(script_dir)
+
+               # Write the scriptlet to a file that we can execute it.
+               try:
+                       f = open(script_file, "wb")
+                       f.write(self.scriptlet)
+                       f.close()
+
+                       # The file is only accessable by root.
+                       os.chmod(script_file, 700)
+               except:
+                       # Remove the file if an error occurs.
+                       try:
+                               os.unlink(script_file)
+                       except OSError:
+                               pass
+
+                       # XXX catch errors and return a beautiful message to the user
+                       raise
+
+               command = [script_file_chroot,] + self.args
+
+               # If we are running in /, we do not need to chroot there.
+               chroot_dir = None
+               if not self.pakfire.path == "/":
+                       chroot_dir = self.pakfire.path
+
+               try:
+                       ret = chroot.do(command, cwd="/tmp",
+                               chrootPath=chroot_path,
+                               personality=self.pakfire.distro.personality,
+                               shell=False,
+                               timeout=SCRIPTLET_TIMEOUT,
+                               logger=logging.getLogger())
+
+               except Error, e:
+                       raise ActionError, _("The scriptlet returned an error:\n%s" % e)
+
+               except commandTimeoutExpired:
+                       raise ActionError, _("The scriptlet ran more than %s seconds and was killed." \
+                               % SCRIPTLET_TIMEOUT)
+
+               finally:
+                       # Remove the script file.
+                       try:
+                               os.unlink(script_file)
+                       except OSError:
+                               logging.debug("Could not remove scriptlet file: %s" % script_file)
 
 
 class ActionScriptPreIn(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["prein",]
 
 
 class ActionScriptPostIn(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["postin",]
 
 
 class ActionScriptPreUn(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["preun",]
 
 
 class ActionScriptPostUn(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["postun",]
 
 
 class ActionScriptPreUp(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["preup",]
 
 
 class ActionScriptPostUp(ActionScript):
-       pass
+       @property
+       def args(self):
+               return ["postup",]
 
 
 class ActionScriptPostTrans(ActionScript):
@@ -102,15 +221,21 @@ class ActionScriptPostTrans(ActionScript):
 
 
 class ActionScriptPostTransIn(ActionScriptPostTrans):
-       pass
+       @property
+       def args(self):
+               return ["posttransin",]
 
 
 class ActionScriptPostTransUn(ActionScriptPostTrans):
-       pass
+       @property
+       def args(self):
+               return ["posttransun",]
 
 
 class ActionScriptPostTransUp(ActionScriptPostTrans):
-       pass
+       @property
+       def args(self):
+               return ["posttransup",]
 
 
 class ActionInstall(Action):
index 37229f8bb611be30647934c43ffa86292788475a..02cdc5dd77c8cb9d6cc46348d4a63fcd771104de 100644 (file)
@@ -35,7 +35,7 @@ CCACHE_CACHE_DIR = os.path.join(CACHE_DIR, "ccache")
 REPO_CACHE_DIR = os.path.join(CACHE_DIR, "repos")
 
 LOCAL_BUILD_REPO_PATH = "/var/lib/pakfire/local"
-LOCAL_TMP_PATH = "/var/tmp/pakfire"
+LOCAL_TMP_PATH = "var/tmp/pakfire"
 
 PACKAGES_DB_DIR = "var/lib/pakfire"
 PACKAGES_DB = os.path.join(PACKAGES_DB_DIR, "packages.db")
@@ -125,3 +125,64 @@ PKG_PAYLOAD_HASH1="%(payload_hash1)s"
 
 # XXX make this configurable in pakfire.conf
 PAKFIRE_MULTIINSTALL = ["kernel",]
+
+SCRIPTLET_INTERPRETER = "/bin/sh"
+SCRIPTLET_TIMEOUT = 60 * 15
+
+SCRIPTLET_TEMPLATE = """\
+#!/bin/sh
+
+function control_prein() {
+%(control_prein)s
+}
+
+function control_postin() {
+%(control_postin)s
+}
+
+function control_preun() {
+%(control_preun)s
+}
+
+function control_postun() {
+%(control_postun)s
+}
+
+function control_preup() {
+%(control_preup)s
+}
+
+function control_postup() {
+%(control_postup)s
+}
+
+function control_postransin() {
+%(control_posttransin)s
+}
+
+function control_posttransun() {
+%(control_posttransun)s
+}
+
+function control_posttransup() {
+%(control_posttransup)s
+}
+
+# Get right action from commandline.
+action=${1}
+shift
+
+case "${action}" in
+       prein|postin|preun|postun|preup|postup|posttransin|posttransun|posttransup)
+               control_${action} $@
+               ;;
+
+       *)
+               echo "Unknown action: ${action}" >&2
+               exit 2
+               ;;
+esac
+
+# Always exit with an okay status.
+exit 0
+"""
index 9bc8d68d86ef026fb9aa796a1ecfb50222377a5f..c5574477d1b7cb2216ccc4effb27c1d0b2866b93 100644 (file)
@@ -21,6 +21,9 @@
 
 from i18n import _
 
+class commandTimeoutExpired(Exception):
+       pass # XXX cannot be as is
+
 class Error(Exception):
        exit_code = 1
 
index 7c19c9fb1841fca38447b1ac3702db0a29157a85..3926f40bd6bb45cd58a22d0222c9729aef3b0e5e 100644 (file)
@@ -137,7 +137,7 @@ class FilePackage(Package):
                return True
 
        def open_archive(self):
-               return tarfile.open(self.filename)
+               return tarfile.open(self.filename, format=tarfile.PAX_FORMAT)
 
        def extract(self, message=None, prefix=None):
                logging.debug("Extracting package %s" % self.friendly_name)
index 4705bbd0c323970bcfa13a4b1cd2ca170eaa0354..61bb9b88a13bdf5dd858c8b12777320004254ef9 100644 (file)
@@ -8,7 +8,7 @@ msgid ""
 msgstr ""
 "Project-Id-Version: PACKAGE VERSION\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-08-07 13:23+0200\n"
+"POT-Creation-Date: 2011-08-10 18:10+0200\n"
 "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
 "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
 "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,24 +17,46 @@ msgstr ""
 "Content-Type: text/plain; charset=CHARSET\n"
 "Content-Transfer-Encoding: 8bit\n"
 
-#: ../pakfire/actions.py:123 ../pakfire/actions.py:180
+#: ../pakfire/actions.py:117
+#, python-format
+msgid "Cannot run scriptlet because no interpreter is available: %s"
+msgstr ""
+
+#: ../pakfire/actions.py:121
+#, python-format
+msgid "Cannot run scriptlet because the interpreter is not executable: %s"
+msgstr ""
+
+#: ../pakfire/actions.py:169
+#, python-format
+msgid ""
+"The scriptlet returned an error:\n"
+"%s"
+msgstr ""
+
+#: ../pakfire/actions.py:172
+#, python-format
+msgid "The scriptlet ran more than %s seconds and was killed."
+msgstr ""
+
+#: ../pakfire/actions.py:248 ../pakfire/actions.py:305
 msgid "Installing"
 msgstr ""
 
-#: ../pakfire/actions.py:133
+#: ../pakfire/actions.py:258
 msgid "Updating"
 msgstr ""
 
-#: ../pakfire/actions.py:147
+#: ../pakfire/actions.py:272
 msgid "Removing"
 msgstr ""
 
 #. Cleaning up leftover files and stuff.
-#: ../pakfire/actions.py:165
+#: ../pakfire/actions.py:290
 msgid "Cleanup"
 msgstr ""
 
-#: ../pakfire/actions.py:190
+#: ../pakfire/actions.py:315
 msgid "Downgrading"
 msgstr ""
 
@@ -288,15 +310,15 @@ msgstr ""
 msgid "Path to input packages."
 msgstr ""
 
-#: ../pakfire/errors.py:27
+#: ../pakfire/errors.py:30
 msgid "An unhandled error occured."
 msgstr ""
 
-#: ../pakfire/errors.py:48
+#: ../pakfire/errors.py:51
 msgid "One or more dependencies could not been resolved."
 msgstr ""
 
-#: ../pakfire/errors.py:63
+#: ../pakfire/errors.py:66
 msgid ""
 "The requested action cannot be done on offline mode.\n"
 "Please connect your system to the network, remove --offline from the command "