[PackageKit-commit] packagekit: Branch 'master' - 22 commits

Richard Hughes hughsient at kemper.freedesktop.org
Mon Jul 13 03:25:33 PDT 2009


 .gitignore                         |    1 
 backends/dummy/pk-backend-dummy.c  |    2 
 backends/portage/portageBackend.py |  170 +++--
 client/pk-console.c                |   14 
 docs/api/.gitignore                |    1 
 docs/security.txt                  |   78 ++
 etc/PackageKit.conf.in             |   35 +
 lib/packagekit-glib/pk-enum.c      |    5 
 lib/packagekit-glib/pk-enum.h      |    7 
 po/pl.po                           |  209 +++---
 src/Makefile.am                    |    6 
 src/gdb.sh                         |    2 
 src/pk-engine.c                    |   13 
 src/pk-engine.h                    |    1 
 src/pk-lsof.c                      |  344 ++++++++++
 src/pk-lsof.h                      |   59 +
 src/pk-post-trans.c                |  889 ----------------------------
 src/pk-post-trans.h                |   63 -
 src/pk-self-test.c                 |    2 
 src/pk-transaction-extra.c         | 1167 +++++++++++++++++++++++++++++++++++++
 src/pk-transaction-extra.h         |   65 ++
 src/pk-transaction-list.c          |   78 ++
 src/pk-transaction-list.h          |    3 
 src/pk-transaction.c               |  317 +++++++++-
 src/pk-transaction.h               |    1 
 25 files changed, 2404 insertions(+), 1128 deletions(-)

New commits:
commit 9a276f6ec7cc270f07aab6f0be5e37891d36d2f0
Author: Richard Hughes <richard at hughsie.com>
Date:   Mon Jul 13 10:45:24 2009 +0100

    Add a security document after some initial review

diff --git a/.gitignore b/.gitignore
index ec41eff..02af524 100644
--- a/.gitignore
+++ b/.gitignore
@@ -51,7 +51,6 @@ py-compile
 tags
 *.patch
 NEWS.new
-*.txt
 .lock-wscript
 .waf-*
 _build_
diff --git a/docs/api/.gitignore b/docs/api/.gitignore
index f580302..3c13afb 100644
--- a/docs/api/.gitignore
+++ b/docs/api/.gitignore
@@ -4,6 +4,7 @@ Makefile.libs
 *.bak
 *.gcno
 *.out
+*.txt
 tmpl
 xml
 html
diff --git a/docs/security.txt b/docs/security.txt
new file mode 100644
index 0000000..5aff5a2
--- /dev/null
+++ b/docs/security.txt
@@ -0,0 +1,78 @@
+			Security and PackageKit
+
+This document is a brief overview of security policies and notes about security
+in the PackageKit project. It has been written by the PackageKit authors, and
+should not be treated as independent analysis. This document has been written as
+packagekitd is a system activated daemon running as the root user (uid 0), which
+means the package management system is run as root also. The daemon receives
+untrusted input from the client, which means the daemon is security sensitive.
+
+First, a high level overview, in this case using the yum backend as an example:
+
+  gpk-update-icon      gpk-application
+        |              _______/
+     [DBUS] __[DBUS]__/
+        |  /
+   packagekitd -- [DBUS] -- polkit-daemon-1
+        |
+      [pipe]
+        |
+   yumBackend.py (using yum)
+
+packagekitd does not expose itself remotely over XMLRPC or other remote
+interface, and so a remote denial of service or exploit is impossible without a
+serious exploit of other services such as DBus. It advertises a simple interface
+that can be queried by clients in unprivileged and privileged modes.
+The privileged modes are controlled using PolicyKit, and policy and the
+authentication mechanism is deferred to the polkit-daemon-1 service.
+
+When a privileged method is executed, the daemon asks polkit-daemon-1 for
+authentication, which then blocks until the authentication is completed. This
+is handled asynchronously, and the daemon can process other requests whilst
+waiting for user input.
+
+The packagekitd daemon is started using DBus system activation, which means it
+is started without any environment (no PATH, etc) and therefore is impossible to
+exploit by preloading other libraries. It is also running as uid 0, and so
+requires root privileges to inject code into it.
+
+A typical transaction would be for the client to request a TID (transaction ID)
+to which the server responds by creating a transaction instance and exposing
+this on the system bus. The client then connects to this interface, and executes
+the chosen method. This method will emit signals such as ::Package(), then
+::Finished() and then after a number of seconds ::Destroy() which will remove
+the interface from the bus.
+
+Attack vectors:
+
+ * A client could cause a local DoS (denial of service) by repeatedly calling
+ GetTid without then calling a method to use this TID. This is mitigated by
+ timing out Tid request after a present number of seconds, and the effect can be
+ limited with a config variable (SimultaneousTransactionsForUid).
+
+ * Local DoS by repeatedly calling non-privileged methods such as Resolve and
+ SearchName. This could be mitigated by limiting the number of requests per
+ second for a certain seat, although no code has been written to do this at
+ present.
+
+ * A privileged method could be requested and then ignored or hidden by the
+ window manager. This is mitigated by not blocking the daemon when processing
+ authentication, and timing-out the authentication after a number of seconds if
+ authentication credentials are not supplied.
+
+ * Passing untrusted input to the backend which could be used to crash and
+ exploit the underlying package system. This is mitigated by rejecting invalid
+ input to methods, and testing filenames for existence before they are passed
+ to the backend.
+
+ * A session service can be written to automatically authenticate methods, and
+ replace the native client. This is hard to mitigate, as as soon as you have
+ untrusted code running in the session, it's very easy to load exploit code
+ using GTK_MODULES into previously trusted applications, such as gpk-application.
+
+ * Issuing a large amount of data to a method to cause a local denial of
+ service, for instance calling Resolve with millions of parameters. This is
+ mitigated in the daemon by checking for a sane number of requests
+ (MaximumPackagesToProcess) for each method, and also limiting the total length
+ of the data.
+
commit d0e14db8ccb34128c624bed157c1b0079169563f
Author: Richard Hughes <richard at hughsie.com>
Date:   Mon Jul 13 10:27:54 2009 +0100

    Limit the number of packages that can be processed by the daemon in one method call

diff --git a/etc/PackageKit.conf.in b/etc/PackageKit.conf.in
index ddc7957..702a6df 100644
--- a/etc/PackageKit.conf.in
+++ b/etc/PackageKit.conf.in
@@ -178,3 +178,20 @@ StateChangedTimeoutNormal=600
 # default=25
 SimultaneousTransactionsForUid=25
 
+# The maximum number of items that can be resolved in one method
+#
+# Setting this lower decreases the risk of a local denial of service, but may
+# cause errors if the desktop client is trying to resolve a large number of
+# packages in one method.
+#
+# default=10
+MaximumItemsToResolve=10
+
+# The maximum number of packages that can be processed in one method
+#
+# Setting this lower decreases the risk of a local denial of service, but may
+# cause errors if the desktop client is trying to do a large transaction.
+#
+# default=2500
+MaximumPackagesToProcess=2500
+
diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index c583c26..c6c534c 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -241,6 +241,7 @@ pk_transaction_error_get_type (void)
 			ENUM_ENTRY (PK_TRANSACTION_ERROR_PACK_INVALID, "PackInvalid"),
 			ENUM_ENTRY (PK_TRANSACTION_ERROR_MIME_TYPE_NOT_SUPPORTED, "MimeTypeNotSupported"),
 			ENUM_ENTRY (PK_TRANSACTION_ERROR_INVALID_PROVIDE, "InvalidProvide"),
+			ENUM_ENTRY (PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID, "NumberOfPackagesInvalid"),
 			{ 0, NULL, NULL }
 		};
 		etype = g_enum_register_static ("PkTransactionError", values);
@@ -2121,6 +2122,8 @@ pk_transaction_download_packages (PkTransaction *transaction, gchar **package_id
 	gchar *package_ids_temp;
 	gchar *directory = NULL;
 	gint retval;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2144,6 +2147,17 @@ pk_transaction_download_packages (PkTransaction *transaction, gchar **package_id
 		goto out;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -2268,6 +2282,8 @@ pk_transaction_get_depends (PkTransaction *transaction, const gchar *filter, gch
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2301,6 +2317,17 @@ pk_transaction_get_depends (PkTransaction *transaction, const gchar *filter, gch
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -2342,6 +2369,8 @@ pk_transaction_get_details (PkTransaction *transaction, gchar **package_ids, DBu
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2367,6 +2396,17 @@ pk_transaction_get_details (PkTransaction *transaction, gchar **package_ids, DBu
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -2456,6 +2496,8 @@ pk_transaction_get_files (PkTransaction *transaction, gchar **package_ids, DBusG
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2481,6 +2523,17 @@ pk_transaction_get_files (PkTransaction *transaction, gchar **package_ids, DBusG
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -2693,6 +2746,8 @@ pk_transaction_get_requires (PkTransaction *transaction, const gchar *filter, gc
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2726,6 +2781,17 @@ pk_transaction_get_requires (PkTransaction *transaction, const gchar *filter, gc
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -2809,6 +2875,8 @@ pk_transaction_get_update_detail (PkTransaction *transaction, gchar **package_id
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -2834,6 +2902,17 @@ pk_transaction_get_update_detail (PkTransaction *transaction, gchar **package_id
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -3132,6 +3211,8 @@ pk_transaction_install_packages (PkTransaction *transaction, gboolean only_trust
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -3157,6 +3238,17 @@ pk_transaction_install_packages (PkTransaction *transaction, gboolean only_trust
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -3332,6 +3424,8 @@ pk_transaction_remove_packages (PkTransaction *transaction, gchar **package_ids,
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -3357,6 +3451,17 @@ pk_transaction_remove_packages (PkTransaction *transaction, gchar **package_ids,
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
@@ -3518,6 +3623,7 @@ pk_transaction_resolve (PkTransaction *transaction, const gchar *filter,
 	gchar *packages_temp;
 	guint i;
 	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -3551,8 +3657,18 @@ pk_transaction_resolve (PkTransaction *transaction, const gchar *filter,
 		return;
 	}
 
-	/* check for sanity */
+	/* check for length sanity */
 	length = g_strv_length (packages);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumItemsToResolve");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_INPUT_INVALID,
+				     "Too many items to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
+	/* check each package for sanity */
 	for (i=0; i<length; i++) {
 		ret = pk_strvalidate (packages[i]);
 		if (!ret) {
@@ -3972,6 +4088,8 @@ pk_transaction_update_packages (PkTransaction *transaction, gboolean only_truste
 	gboolean ret;
 	GError *error;
 	gchar *package_ids_temp;
+	guint length;
+	guint max_length;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
@@ -3997,6 +4115,17 @@ pk_transaction_update_packages (PkTransaction *transaction, gboolean only_truste
 		return;
 	}
 
+	/* check for length sanity */
+	length = g_strv_length (package_ids);
+	max_length = pk_conf_get_int (transaction->priv->conf, "MaximumPackagesToProcess");
+	if (length > max_length) {
+		error = g_error_new (PK_TRANSACTION_ERROR, PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
+				     "Too many packages to process (%i/%i)", length, max_length);
+		pk_transaction_release_tid (transaction);
+		pk_transaction_dbus_return_error (context, error);
+		return;
+	}
+
 	/* check package_ids */
 	ret = pk_package_ids_check (package_ids);
 	if (!ret) {
diff --git a/src/pk-transaction.h b/src/pk-transaction.h
index 086f793..38cdf49 100644
--- a/src/pk-transaction.h
+++ b/src/pk-transaction.h
@@ -73,6 +73,7 @@ typedef enum
 	PK_TRANSACTION_ERROR_INVALID_PROVIDE,
 	PK_TRANSACTION_ERROR_PACK_INVALID,
 	PK_TRANSACTION_ERROR_MIME_TYPE_NOT_SUPPORTED,
+	PK_TRANSACTION_ERROR_NUMBER_OF_PACKAGES_INVALID,
 	PK_TRANSACTION_ERROR_LAST
 } PkTransactionError;
 
commit 316d771f9e150e115de1de980ff33c7939c7e99e
Author: Richard Hughes <richard at hughsie.com>
Date:   Mon Jul 13 09:50:21 2009 +0100

    Limit the maximum number of requests a given user is able to request and queue

diff --git a/etc/PackageKit.conf.in b/etc/PackageKit.conf.in
index 259a752..ddc7957 100644
--- a/etc/PackageKit.conf.in
+++ b/etc/PackageKit.conf.in
@@ -169,3 +169,12 @@ StateChangedTimeoutPriority=5
 # default=600
 StateChangedTimeoutNormal=600
 
+# The maximum number of requests a given user is able to request and queue
+#
+# Setting this lower decreases the risk of a local denial of service, but may
+# cause errors if the desktop client is doing many requests to the daemon in a
+# short period of time.
+#
+# default=25
+SimultaneousTransactionsForUid=25
+
diff --git a/src/pk-engine.c b/src/pk-engine.c
index 532a8dd..d5ab3ef 100644
--- a/src/pk-engine.c
+++ b/src/pk-engine.c
@@ -149,6 +149,7 @@ pk_engine_error_get_type (void)
 			ENUM_ENTRY (PK_ENGINE_ERROR_REFUSED_BY_POLICY, "RefusedByPolicy"),
 			ENUM_ENTRY (PK_ENGINE_ERROR_CANNOT_SET_PROXY, "CannotSetProxy"),
 			ENUM_ENTRY (PK_ENGINE_ERROR_NOT_SUPPORTED, "NotSupported"),
+			ENUM_ENTRY (PK_ENGINE_ERROR_CANNOT_ALLOCATE_TID, "CannotAllocateTid"),
 			{ 0, NULL, NULL }
 		};
 		etype = g_enum_register_static ("PkEngineError", values);
@@ -239,6 +240,8 @@ pk_engine_get_tid (PkEngine *engine, DBusGMethodInvocation *context)
 	gchar *new_tid;
 	gboolean ret;
 	gchar *sender = NULL;
+	GError *error;
+	GError *error_local = NULL;
 
 	g_return_if_fail (PK_IS_ENGINE (engine));
 
@@ -246,7 +249,14 @@ pk_engine_get_tid (PkEngine *engine, DBusGMethodInvocation *context)
 	sender = dbus_g_method_get_sender (context);
 	new_tid = pk_transaction_db_generate_id (engine->priv->transaction_db);
 
-	ret = pk_transaction_list_create (engine->priv->transaction_list, new_tid, sender);
+	ret = pk_transaction_list_create (engine->priv->transaction_list, new_tid, sender, &error_local);
+	if (!ret) {
+		error = g_error_new (PK_ENGINE_ERROR, PK_ENGINE_ERROR_CANNOT_ALLOCATE_TID, "getting the tid failed: %s", error_local->message);
+		dbus_g_method_return_error (context, error);
+		g_error_free (error_local);
+		goto out;
+	}
+
 	egg_debug ("sending tid: '%s'", new_tid);
 
 	/* reset the timer */
@@ -254,6 +264,7 @@ pk_engine_get_tid (PkEngine *engine, DBusGMethodInvocation *context)
 
 	/* return TID */
 	dbus_g_method_return (context, new_tid);
+out:
 	g_free (new_tid);
 	g_free (sender);
 }
diff --git a/src/pk-engine.h b/src/pk-engine.h
index f45b5fa..8096f3a 100644
--- a/src/pk-engine.h
+++ b/src/pk-engine.h
@@ -60,6 +60,7 @@ typedef enum
 	PK_ENGINE_ERROR_REFUSED_BY_POLICY,
 	PK_ENGINE_ERROR_CANNOT_SET_PROXY,
 	PK_ENGINE_ERROR_NOT_SUPPORTED,
+	PK_ENGINE_ERROR_CANNOT_ALLOCATE_TID,
 	PK_ENGINE_ERROR_LAST
 } PkEngineError;
 
diff --git a/src/pk-transaction-list.c b/src/pk-transaction-list.c
index 0fb15b2..03234ea 100644
--- a/src/pk-transaction-list.c
+++ b/src/pk-transaction-list.c
@@ -42,6 +42,7 @@
 #include "egg-debug.h"
 #include "egg-string.h"
 
+#include "pk-conf.h"
 #include "pk-transaction-list.h"
 #include "org.freedesktop.PackageKit.Transaction.h"
 
@@ -63,6 +64,7 @@ struct PkTransactionListPrivate
 	GPtrArray		*array;
 	guint			 unwedge1_id;
 	guint			 unwedge2_id;
+	PkConf			*conf;
 };
 
 typedef struct {
@@ -76,6 +78,7 @@ typedef struct {
 	guint			 idle_id;
 	guint			 commit_id;
 	gulong			 finished_id;
+	guint			 uid;
 } PkTransactionItem;
 
 enum {
@@ -350,12 +353,37 @@ pk_transaction_list_no_commit_cb (PkTransactionItem *item)
 }
 
 /**
+ * pk_transaction_list_get_number_transactions_for_uid:
+ *
+ * Find all the transactions that are pending from this uid.
+ **/
+static guint
+pk_transaction_list_get_number_transactions_for_uid (PkTransactionList *tlist, guint uid)
+{
+	guint i;
+	GPtrArray *array;
+	PkTransactionItem *item;
+	guint count = 0;
+
+	/* find all the transactions in progress */
+	array = tlist->priv->array;
+	for (i=0; i<array->len; i++) {
+		item = (PkTransactionItem *) g_ptr_array_index (array, i);
+		if (item->uid == uid)
+			count++;
+	}
+	return count;
+}
+
+/**
  * pk_transaction_list_create:
  **/
 gboolean
-pk_transaction_list_create (PkTransactionList *tlist, const gchar *tid, const gchar *sender)
+pk_transaction_list_create (PkTransactionList *tlist, const gchar *tid, const gchar *sender, GError **error)
 {
-	gboolean ret;
+	guint count;
+	guint max_count;
+	gboolean ret = FALSE;
 	PkTransactionItem *item;
 	DBusGConnection *connection;
 
@@ -365,8 +393,10 @@ pk_transaction_list_create (PkTransactionList *tlist, const gchar *tid, const gc
 	/* already added? */
 	item = pk_transaction_list_get_from_tid (tlist, tid);
 	if (item != NULL) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "already added %s to list", tid);
 		egg_warning ("already added %s to list", tid);
-		return FALSE;
+		goto out;
 	}
 
 	/* add to the array */
@@ -394,13 +424,42 @@ pk_transaction_list_create (PkTransactionList *tlist, const gchar *tid, const gc
 
 	/* set the TID on the transaction */
 	ret = pk_transaction_set_tid (item->transaction, item->tid);
-	if (!ret)
-		egg_error ("failed to set TID");
+	if (!ret) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to set TID: %s", tid);
+		goto out;
+	}
 
 	/* set the DBUS sender on the transaction */
 	ret = pk_transaction_set_sender (item->transaction, sender);
-	if (!ret)
-		egg_error ("failed to set sender");
+	if (!ret) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to set sender: %s", tid);
+		goto out;
+	}
+
+	/* get the uid for the transaction */
+	g_object_get (item->transaction,
+		      "uid", &item->uid,
+		      NULL);
+
+	/* find out the number of transactions this uid already has in progress */
+	count = pk_transaction_list_get_number_transactions_for_uid (tlist, item->uid);
+	egg_debug ("uid=%i, count=%i", item->uid, count);
+
+	/* would this take us over the maximum number of requests allowed */
+	max_count = pk_conf_get_int (tlist->priv->conf, "SimultaneousTransactionsForUid");
+	if (count > max_count) {
+		if (error != NULL)
+			*error = g_error_new (1, 0, "failed to allocate %s as uid %i already has %i transactions in progress", tid, item->uid, count);
+
+		/* free transaction, as it's never going to be added */
+		pk_transaction_list_item_free (item);
+
+		/* failure */
+		ret = FALSE;
+		goto out;
+	}
 
 	/* put on the bus */
 	dbus_g_object_type_install_info (PK_TYPE_TRANSACTION, &dbus_glib_pk_transaction_object_info);
@@ -412,7 +471,8 @@ pk_transaction_list_create (PkTransactionList *tlist, const gchar *tid, const gc
 
 	egg_debug ("adding transaction %p, item %p", item->transaction, item);
 	g_ptr_array_add (tlist->priv->array, item);
-	return TRUE;
+out:
+	return ret;
 }
 
 /**
@@ -745,6 +805,7 @@ static void
 pk_transaction_list_init (PkTransactionList *tlist)
 {
 	tlist->priv = PK_TRANSACTION_LIST_GET_PRIVATE (tlist);
+	tlist->priv->conf = pk_conf_new ();
 	tlist->priv->array = g_ptr_array_new ();
 	tlist->priv->unwedge2_id = 0;
 	tlist->priv->unwedge1_id = g_timeout_add_seconds (PK_TRANSACTION_WEDGE_CHECK, (GSourceFunc) pk_transaction_list_wedge_check1, tlist);
@@ -772,6 +833,7 @@ pk_transaction_list_finalize (GObject *object)
 
 	g_ptr_array_foreach (tlist->priv->array, (GFunc) pk_transaction_list_item_free, NULL);
 	g_ptr_array_free (tlist->priv->array, TRUE);
+	g_object_unref (tlist->priv->conf);
 
 	G_OBJECT_CLASS (pk_transaction_list_parent_class)->finalize (object);
 }
diff --git a/src/pk-transaction-list.h b/src/pk-transaction-list.h
index 07e9862..83221f0 100644
--- a/src/pk-transaction-list.h
+++ b/src/pk-transaction-list.h
@@ -56,7 +56,8 @@ PkTransactionList *pk_transaction_list_new		(void);
 
 gboolean	 pk_transaction_list_create		(PkTransactionList	*tlist,
 							 const gchar		*tid,
-							 const gchar		*sender);
+							 const gchar		*sender,
+							 GError			**error);
 gboolean	 pk_transaction_list_remove		(PkTransactionList	*tlist,
 							 const gchar		*tid);
 gboolean	 pk_transaction_list_commit		(PkTransactionList	*tlist,
commit 0bbc51bf285d89abf7b979961897be151d65b1ed
Author: Richard Hughes <richard at hughsie.com>
Date:   Mon Jul 13 09:45:27 2009 +0100

    Add a uid property to the PkTransaction object for future use

diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index 697fbc6..c583c26 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -184,6 +184,13 @@ enum {
 	PK_TRANSACTION_LAST_SIGNAL
 };
 
+enum
+{
+	PROP_0,
+	PROP_UID,
+	PROP_LAST
+};
+
 static guint	     signals [PK_TRANSACTION_LAST_SIGNAL] = { 0 };
 
 G_DEFINE_TYPE (PkTransaction, pk_transaction, G_TYPE_OBJECT)
@@ -4154,6 +4161,26 @@ pk_transaction_what_provides (PkTransaction *transaction, const gchar *filter, c
 }
 
 /**
+ * pk_transaction_get_property:
+ **/
+static void
+pk_transaction_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
+{
+	PkTransaction *transaction;
+
+	transaction = PK_TRANSACTION (object);
+
+	switch (prop_id) {
+	case PROP_UID:
+		g_value_set_uint (value, transaction->priv->uid);
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		break;
+	}
+}
+
+/**
  * pk_transaction_class_init:
  * @klass: The PkTransactionClass
  **/
@@ -4163,6 +4190,15 @@ pk_transaction_class_init (PkTransactionClass *klass)
 	GObjectClass *object_class = G_OBJECT_CLASS (klass);
 	object_class->dispose = pk_transaction_dispose;
 	object_class->finalize = pk_transaction_finalize;
+	object_class->get_property = pk_transaction_get_property;
+
+	g_object_class_install_property (object_class,
+					 PROP_UID,
+					 g_param_spec_uint ("uid",
+							    "UID",
+							    "User ID that created the transaction",
+							    0, G_MAXUINT, 0,
+							    G_PARAM_READABLE));
 
 	signals [PK_TRANSACTION_ALLOW_CANCEL] =
 		g_signal_new ("allow-cancel",
commit 9e4a6fed9c20e317c3a86cd4a425d9de55341266
Author: raven <raven at fedoraproject.org>
Date:   Sun Jul 12 19:32:20 2009 +0000

    Sending translation for Polish

diff --git a/po/pl.po b/po/pl.po
index e2f4a42..97230e8 100644
--- a/po/pl.po
+++ b/po/pl.po
@@ -5,8 +5,8 @@ msgid ""
 msgstr ""
 "Project-Id-Version: pl\n"
 "Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2009-06-30 14:32+0000\n"
-"PO-Revision-Date: 2009-06-30 17:53+0200\n"
+"POT-Creation-Date: 2009-07-12 16:41+0000\n"
+"PO-Revision-Date: 2009-07-12 21:31+0200\n"
 "Last-Translator: Piotr DrÄ…g <piotrdrag at gmail.com>\n"
 "Language-Team: Polish <pl at li.org>\n"
 "MIME-Version: 1.0\n"
@@ -205,41 +205,70 @@ msgstr "Ponowne uruchomienie systemu jest wymagane przez:"
 msgid "Session restart required:"
 msgstr "Wymagane jest ponowne uruchomienie sesji:"
 
-#. TRANSLATORS: a package requires the application to be restarted
+#. TRANSLATORS: a package requires the system to be restarted due to a security update
 #: ../client/pk-console.c:523
+msgid "System restart (security) required by:"
+msgstr ""
+"Ponowne uruchomienie systemu jest wymagane przez:"
+
+#. TRANSLATORS: a package requires the session to be restarted due to a security update
+#: ../client/pk-console.c:526
+msgid "Session restart (security) required:"
+msgstr "Wymagane jest ponowne uruchomienie sesji (z powodu bezpieczeństwa):"
+
+#. TRANSLATORS: a package requires the application to be restarted
+#: ../client/pk-console.c:529
 msgid "Application restart required by:"
 msgstr "Ponowne uruchomienie programu jest wymagane przez:"
 
-#. TRANSLATORS: a package needs to restart they system
-#: ../client/pk-console.c:578
+#. TRANSLATORS: a package needs to restart their system
+#: ../client/pk-console.c:584
 msgid "Please restart the computer to complete the update."
 msgstr "Proszę uruchomić ponownie komputer, aby zakończyć aktualizację."
 
 #. TRANSLATORS: a package needs to restart the session
-#: ../client/pk-console.c:581
+#: ../client/pk-console.c:587
 msgid "Please logout and login to complete the update."
 msgstr "Proszę wylogować się i zalogować, aby zakończyć aktualizację."
 
 #. TRANSLATORS: a package needs to restart the application
-#: ../client/pk-console.c:584
+#: ../client/pk-console.c:590
 msgid "Please restart the application as it is being used."
 msgstr "Proszę uruchomić program ponownie, ponieważ jest używany."
 
+#. TRANSLATORS: a package needs to restart their system (due to security)
+#: ../client/pk-console.c:593
+msgid ""
+"Please restart the computer to complete the update as important security "
+"updates have been installed."
+msgstr ""
+"Proszę uruchomić ponownie komputer, aby zakończyć aktualizację, ponieważ "
+"zainstalowano aktualizacje bezpieczeństwa."
+
+#. TRANSLATORS: a package needs to restart the session (due to security)
+#: ../client/pk-console.c:596
+msgid ""
+"Please logout and login to complete the update as important security updates "
+"have been installed."
+msgstr ""
+"Proszę wylogować się i zalogować, aby zakończyć aktualizację, ponieważ "
+"zainstalowano aktualizacje bezpieczeństwa."
+
 #. TRANSLATORS: The package is already installed on the system
-#: ../client/pk-console.c:711
+#: ../client/pk-console.c:723
 #, c-format
 msgid "The package %s is already installed"
 msgstr "Pakiet %s jest już zainstalowany"
 
 #. TRANSLATORS: The package name was not found in any software sources. The detailed error follows
-#: ../client/pk-console.c:719
+#: ../client/pk-console.c:731
 #, c-format
 msgid "The package %s could not be installed: %s"
 msgstr "Nie można zainstalować pakietu %s: %s"
 
 #. TRANSLATORS: There was a programming error that shouldn't happen. The detailed error follows
-#: ../client/pk-console.c:744 ../client/pk-console.c:767
-#: ../client/pk-console.c:863 ../client/pk-console.c:980
+#: ../client/pk-console.c:756 ../client/pk-console.c:779
+#: ../client/pk-console.c:875 ../client/pk-console.c:992
 #: ../client/pk-tools-common.c:62 ../client/pk-tools-common.c:81
 #: ../client/pk-tools-common.c:89
 #, c-format
@@ -247,254 +276,254 @@ msgid "Internal error: %s"
 msgstr "Wewnętrzny błąd: %s"
 
 #. TRANSLATORS: There was an error installing the packages. The detailed error follows
-#: ../client/pk-console.c:752 ../client/pk-console.c:1376
+#: ../client/pk-console.c:764 ../client/pk-console.c:1388
 #, c-format
 msgid "This tool could not install the packages: %s"
 msgstr "To narzędzie nie może zainstalować pakietów: %s"
 
 #. TRANSLATORS: There was an error installing the files. The detailed error follows
-#: ../client/pk-console.c:775
+#: ../client/pk-console.c:787
 #, c-format
 msgid "This tool could not install the files: %s"
 msgstr "To narzędzie nie może zainstalować plików: %s"
 
 #. TRANSLATORS: The package name was not found in the installed list. The detailed error follows
-#: ../client/pk-console.c:831
+#: ../client/pk-console.c:843
 #, c-format
 msgid "This tool could not remove %s: %s"
 msgstr "To narzędzie nie może usunąć %s: %s"
 
 #. TRANSLATORS: There was an error removing the packages. The detailed error follows
-#: ../client/pk-console.c:854 ../client/pk-console.c:892
-#: ../client/pk-console.c:925
+#: ../client/pk-console.c:866 ../client/pk-console.c:904
+#: ../client/pk-console.c:937
 #, c-format
 msgid "This tool could not remove the packages: %s"
 msgstr "To narzędzie nie może usunąć pakietów: %s"
 
 #. TRANSLATORS: When removing, we might have to remove other dependencies
-#: ../client/pk-console.c:904
+#: ../client/pk-console.c:916
 msgid "The following packages have to be removed:"
 msgstr "Następujące pakiety muszą zostać usunięte:"
 
 #. TRANSLATORS: We are checking if it's okay to remove a list of packages
-#: ../client/pk-console.c:911
+#: ../client/pk-console.c:923
 msgid "Proceed removing additional packages?"
 msgstr "Kontynuować usuwanie dodatkowych pakietów?"
 
 #. TRANSLATORS: We did not remove any packages
-#: ../client/pk-console.c:916
+#: ../client/pk-console.c:928
 msgid "The package removal was canceled!"
 msgstr "Anulowano usunięcie pakietu!"
 
 #. TRANSLATORS: The package name was not found in any software sources
-#: ../client/pk-console.c:957
+#: ../client/pk-console.c:969
 #, c-format
 msgid "This tool could not download the package %s as it could not be found"
 msgstr "To narzędzie nie może pobrać pakietu %s, ponieważ nie można go znaleźć"
 
 #. TRANSLATORS: Could not download the packages for some reason. The detailed error follows
-#: ../client/pk-console.c:988
+#: ../client/pk-console.c:1000
 #, c-format
 msgid "This tool could not download the packages: %s"
 msgstr "To narzędzie nie może pobrać pakietów: %s"
 
 #. TRANSLATORS: There was an error getting the list of files for the package. The detailed error follows
-#: ../client/pk-console.c:1015 ../client/pk-console.c:1024
+#: ../client/pk-console.c:1027 ../client/pk-console.c:1036
 #, c-format
 msgid "This tool could not update %s: %s"
 msgstr "To narzędzie nie może zaktualizować %s: %s"
 
 #. TRANSLATORS: There was an error getting the list of files for the package. The detailed error follows
-#: ../client/pk-console.c:1046 ../client/pk-console.c:1054
+#: ../client/pk-console.c:1058 ../client/pk-console.c:1066
 #, c-format
 msgid "This tool could not get the requirements for %s: %s"
 msgstr "To narzędzie nie może uzyskać wymagań dla %s: %s"
 
 #. TRANSLATORS: There was an error getting the dependencies for the package. The detailed error follows
-#: ../client/pk-console.c:1076 ../client/pk-console.c:1084
+#: ../client/pk-console.c:1088 ../client/pk-console.c:1096
 #, c-format
 msgid "This tool could not get the dependencies for %s: %s"
 msgstr "To narzędzie nie może uzyskać zależności dla %s: %s"
 
 #. TRANSLATORS: There was an error getting the details about the package. The detailed error follows
-#: ../client/pk-console.c:1106 ../client/pk-console.c:1114
+#: ../client/pk-console.c:1118 ../client/pk-console.c:1126
 #, c-format
 msgid "This tool could not get package details for %s: %s"
 msgstr "To narzędzie nie może uzyskać szczegółów pakietu %s: %s"
 
 #. TRANSLATORS: The package name was not found in any software sources. The detailed error follows
-#: ../client/pk-console.c:1136
+#: ../client/pk-console.c:1148
 #, c-format
 msgid "This tool could not find the files for %s: %s"
 msgstr "To narzędzie nie może znaleźć plików dla %s: %s"
 
 #. TRANSLATORS: There was an error getting the list of files for the package. The detailed error follows
-#: ../client/pk-console.c:1144
+#: ../client/pk-console.c:1156
 #, c-format
 msgid "This tool could not get the file list for %s: %s"
 msgstr "To narzędzie nie może uzyskać listy plików dla %s: %s"
 
 #. TRANSLATORS: There was an error getting the list of packages. The filename follows
-#: ../client/pk-console.c:1166
+#: ../client/pk-console.c:1178
 #, c-format
 msgid "File already exists: %s"
 msgstr "Plik już istnieje: %s"
 
 #. TRANSLATORS: follows a list of packages to install
-#: ../client/pk-console.c:1171 ../client/pk-console.c:1227
-#: ../client/pk-console.c:1302
+#: ../client/pk-console.c:1183 ../client/pk-console.c:1239
+#: ../client/pk-console.c:1314
 msgid "Getting package list"
 msgstr "Pobieranie listy pakietów"
 
 #. TRANSLATORS: There was an error getting the list of packages. The detailed error follows
-#: ../client/pk-console.c:1177 ../client/pk-console.c:1233
-#: ../client/pk-console.c:1308
+#: ../client/pk-console.c:1189 ../client/pk-console.c:1245
+#: ../client/pk-console.c:1320
 #, c-format
 msgid "This tool could not get package list: %s"
 msgstr "To narzędzie nie może pobrać listy pakietów: %s"
 
 #. TRANSLATORS: There was an error saving the list
-#: ../client/pk-console.c:1188
+#: ../client/pk-console.c:1200
 #, c-format
 msgid "Failed to save to disk"
 msgstr "Zapisanie na dysku nie powiodło się"
 
 #. TRANSLATORS: There was an error getting the list. The filename follows
-#: ../client/pk-console.c:1222 ../client/pk-console.c:1297
+#: ../client/pk-console.c:1234 ../client/pk-console.c:1309
 #, c-format
 msgid "File does not exist: %s"
 msgstr "Plik nie istnieje: %s"
 
 #. TRANSLATORS: header to a list of packages newly added
-#: ../client/pk-console.c:1254
+#: ../client/pk-console.c:1266
 msgid "Packages to add"
 msgstr "Pakiety do dodania"
 
 #. TRANSLATORS: header to a list of packages removed
-#: ../client/pk-console.c:1262
+#: ../client/pk-console.c:1274
 msgid "Packages to remove"
 msgstr "Pakiety do usunięcia"
 
 #. TRANSLATORS: We didn't find any differences
-#: ../client/pk-console.c:1330
+#: ../client/pk-console.c:1342
 #, c-format
 msgid "No new packages need to be installed"
 msgstr "Nie trzeba instalować nowych pakietów"
 
 #. TRANSLATORS: follows a list of packages to install
-#: ../client/pk-console.c:1336
+#: ../client/pk-console.c:1348
 msgid "To install"
 msgstr "Do zainstalowania"
 
 #. TRANSLATORS: searching takes some time....
-#: ../client/pk-console.c:1348
+#: ../client/pk-console.c:1360
 msgid "Searching for package: "
 msgstr "Wyszukiwanie pakietu: "
 
 #. TRANSLATORS: package was not found -- this is the end of a string ended in ...
-#: ../client/pk-console.c:1352
+#: ../client/pk-console.c:1364
 msgid "not found."
 msgstr "nie znaleziono."
 
 #. TRANSLATORS: We didn't find any packages to install
-#: ../client/pk-console.c:1363
+#: ../client/pk-console.c:1375
 #, c-format
 msgid "No packages can be found to install"
 msgstr "Nie można znaleźć pakietów do zainstalowania"
 
 #. TRANSLATORS: installing new packages from package list
 #. TRANSLATORS: we are now installing the debuginfo packages we found earlier
-#: ../client/pk-console.c:1369
+#: ../client/pk-console.c:1381
 #: ../contrib/debuginfo-install/pk-debuginfo-install.c:868
 #, c-format
 msgid "Installing packages"
 msgstr "Instalowanie pakietów"
 
 #. TRANSLATORS: The package name was not found in any software sources. The detailed error follows
-#: ../client/pk-console.c:1405
+#: ../client/pk-console.c:1417
 #, c-format
 msgid "This tool could not find the update details for %s: %s"
 msgstr "To narzędzie nie może znaleźć szczegółów aktualizacji dla %s: %s"
 
 #. TRANSLATORS: There was an error getting the details about the update for the package. The detailed error follows
-#: ../client/pk-console.c:1413
+#: ../client/pk-console.c:1425
 #, c-format
 msgid "This tool could not get the update details for %s: %s"
 msgstr "To narzędzie nie może uzyskać szczegółów aktualizacji dla %s: %s"
 
 #. TRANSLATORS: This was an unhandled error, and we don't have _any_ context
-#: ../client/pk-console.c:1444
+#: ../client/pk-console.c:1456
 msgid "Error:"
 msgstr "BÅ‚Ä…d:"
 
 #. TRANSLATORS: This a list of details about the package
-#: ../client/pk-console.c:1458
+#: ../client/pk-console.c:1470
 msgid "Package description"
 msgstr "Opis pakietu"
 
 #. TRANSLATORS: This a message (like a little note that may be of interest) from the transaction
-#: ../client/pk-console.c:1474
+#: ../client/pk-console.c:1486
 msgid "Message:"
 msgstr "Komunikat:"
 
 #. TRANSLATORS: This a list files contained in the package
-#: ../client/pk-console.c:1502
+#: ../client/pk-console.c:1514
 msgid "Package files"
 msgstr "Pliki pakietu"
 
 #. TRANSLATORS: This where the package has no files
-#: ../client/pk-console.c:1510
+#: ../client/pk-console.c:1522
 msgid "No files"
 msgstr "Brak plików"
 
 #. TRANSLATORS: This a request for a GPG key signature from the backend, which the client will prompt for later
-#: ../client/pk-console.c:1533
+#: ../client/pk-console.c:1545
 msgid "Repository signature required"
 msgstr "Wymagany jest podpis repozytorium"
 
 #. TRANSLATORS: This a prompt asking the user to import the security key
-#: ../client/pk-console.c:1543
+#: ../client/pk-console.c:1555
 msgid "Do you accept this signature?"
 msgstr "Zaakceptować ten podpis?"
 
 #. TRANSLATORS: This is where the user declined the security key
-#: ../client/pk-console.c:1547
+#: ../client/pk-console.c:1559
 msgid "The signature was not accepted."
 msgstr "Podpis nie został zaakceptowany."
 
 #. TRANSLATORS: This a request for a EULA
-#: ../client/pk-console.c:1581
+#: ../client/pk-console.c:1593
 msgid "End user license agreement required"
 msgstr "Licencja jest wymagana"
 
 #. TRANSLATORS: This a prompt asking the user to agree to the license
-#: ../client/pk-console.c:1588
+#: ../client/pk-console.c:1600
 msgid "Do you agree to this license?"
 msgstr "Zaakceptować tę licencję?"
 
 #. TRANSLATORS: This is where the user declined the license
-#: ../client/pk-console.c:1592
+#: ../client/pk-console.c:1604
 msgid "The license was refused."
 msgstr "Odrzucono licencjÄ™."
 
 #. TRANSLATORS: This is when the daemon crashed, and we are up shit creek without a paddle
-#: ../client/pk-console.c:1621
+#: ../client/pk-console.c:1633
 msgid "The daemon crashed mid-transaction!"
 msgstr "Demon zawiesił się w połowie transakcji!"
 
 #. TRANSLATORS: This is the header to the --help menu
-#: ../client/pk-console.c:1674
+#: ../client/pk-console.c:1686
 msgid "PackageKit Console Interface"
 msgstr "Interfejs konsoli PackageKit"
 
 #. these are commands we can use with pkcon
-#: ../client/pk-console.c:1676
+#: ../client/pk-console.c:1688
 msgid "Subcommands:"
 msgstr "Podpolecenia:"
 
 #. TRANSLATORS: command line argument, if we should show debugging information
 #. TRANSLATORS: if we should show debugging data
-#: ../client/pk-console.c:1768 ../client/pk-generate-pack.c:185
+#: ../client/pk-console.c:1780 ../client/pk-generate-pack.c:185
 #: ../client/pk-monitor.c:125
 #: ../contrib/command-not-found/pk-command-not-found.c:521
 #: ../contrib/debuginfo-install/pk-debuginfo-install.c:532
@@ -503,149 +532,149 @@ msgid "Show extra debugging information"
 msgstr "Wyświetla dodatkowe informacje o debugowaniu"
 
 #. TRANSLATORS: command line argument, just show the version string
-#: ../client/pk-console.c:1771 ../client/pk-monitor.c:127
+#: ../client/pk-console.c:1783 ../client/pk-monitor.c:127
 msgid "Show the program version and exit"
 msgstr "Wyświetla wersję programu i wyłącza"
 
 #. TRANSLATORS: command line argument, use a filter to narrow down results
-#: ../client/pk-console.c:1774
+#: ../client/pk-console.c:1786
 msgid "Set the filter, e.g. installed"
 msgstr "Ustawia filtr, np. zainstalowane"
 
 #. TRANSLATORS: command line argument, work asynchronously
-#: ../client/pk-console.c:1777
+#: ../client/pk-console.c:1789
 msgid "Exit without waiting for actions to complete"
 msgstr "Wyłącza bez oczekiwania na zakończenie działań"
 
 #. TRANSLATORS: This is when we could not connect to the system bus, and is fatal
-#: ../client/pk-console.c:1804
+#: ../client/pk-console.c:1816
 msgid "This tool could not connect to system DBUS."
 msgstr "To narzędzie nie może połączyć się z systemowym D-Bus."
 
 #. TRANSLATORS: The user specified an incorrect filter
-#: ../client/pk-console.c:1893
+#: ../client/pk-console.c:1905
 msgid "The filter specified was invalid"
 msgstr "Podany filtr jest nieprawidłowy"
 
 #. TRANSLATORS: a search type can be name, details, file, etc
-#: ../client/pk-console.c:1911
+#: ../client/pk-console.c:1923
 msgid "A search type is required, e.g. name"
 msgstr "Wymagany jest typ wyszukiwania, np. nazwa"
 
 #. TRANSLATORS: the user needs to provide a search term
-#: ../client/pk-console.c:1917 ../client/pk-console.c:1925
-#: ../client/pk-console.c:1933 ../client/pk-console.c:1941
+#: ../client/pk-console.c:1929 ../client/pk-console.c:1937
+#: ../client/pk-console.c:1945 ../client/pk-console.c:1953
 msgid "A search term is required"
 msgstr "Wymagany jest wyszukiwany termin"
 
 #. TRANSLATORS: the search type was provided, but invalid
-#: ../client/pk-console.c:1947
+#: ../client/pk-console.c:1959
 msgid "Invalid search type"
 msgstr "Nieprawidłowy typ wyszukiwania"
 
 #. TRANSLATORS: the user did not specify what they wanted to install
-#: ../client/pk-console.c:1953
+#: ../client/pk-console.c:1965
 msgid "A package name or filename to install is required"
 msgstr "Wymagana jest nazwa pakietu lub pliku do zainstalowania"
 
 #. TRANSLATORS: geeky error, 99.9999% of users won't see this
-#: ../client/pk-console.c:1961
+#: ../client/pk-console.c:1973
 msgid "A type, key_id and package_id are required"
 msgstr "Wymagany jest typ, key_id i package_id"
 
 #. TRANSLATORS: the user did not specify what they wanted to remove
-#: ../client/pk-console.c:1969
+#: ../client/pk-console.c:1981
 msgid "A package name to remove is required"
 msgstr "Wymagana jest nazwa pakietu do usunięcia"
 
 #. TRANSLATORS: the user did not specify anything about what to download or where
-#: ../client/pk-console.c:1976
+#: ../client/pk-console.c:1988
 msgid ""
 "A destination directory and then the package names to download are required"
 msgstr "Wymagany jest katalog docelowy, a następnie nazwy pakietów do pobrania"
 
 #. TRANSLATORS: the directory does not exist, so we can't continue
-#: ../client/pk-console.c:1982
+#: ../client/pk-console.c:1994
 msgid "Directory not found"
 msgstr "Nie znaleziono katalogu"
 
 #. TRANSLATORS: geeky error, 99.9999% of users won't see this
-#: ../client/pk-console.c:1989
+#: ../client/pk-console.c:2001
 msgid "A licence identifier (eula-id) is required"
 msgstr "Wymagany jest identyfikator licencji (eula-id)"
 
 #. TRANSLATORS: geeky error, 99.9999% of users won't see this
-#: ../client/pk-console.c:1998
+#: ../client/pk-console.c:2010
 msgid "A transaction identifier (tid) is required"
 msgstr "Wymagany jest identyfikator transakcji (tid)"
 
 #. TRANSLATORS: The user did not specify a package name
-#: ../client/pk-console.c:2014
+#: ../client/pk-console.c:2026
 msgid "A package name to resolve is required"
 msgstr "Wymagana jest nazwa pakietu do rozwiÄ…zania"
 
 #. TRANSLATORS: The user did not specify a repository (software source) name
-#: ../client/pk-console.c:2022 ../client/pk-console.c:2030
+#: ../client/pk-console.c:2034 ../client/pk-console.c:2042
 msgid "A repository name is required"
 msgstr "Wymagana jest nazwa repozytorium"
 
 #. TRANSLATORS: The user didn't provide any data
-#: ../client/pk-console.c:2038
+#: ../client/pk-console.c:2050
 msgid "A repo name, parameter and value are required"
 msgstr "Wymagana jest nazwa, parametr i wartość repozytorium"
 
 #. TRANSLATORS: The user didn't specify what action to use
-#: ../client/pk-console.c:2051
+#: ../client/pk-console.c:2063
 msgid "An action, e.g. 'update-system' is required"
 msgstr "Wymagane jest działanie, np. \"update-system\""
 
 #. TRANSLATORS: The user specified an invalid action
-#: ../client/pk-console.c:2057
+#: ../client/pk-console.c:2069
 msgid "A correct role is required"
 msgstr "Wymagana jest bieżąca rola"
 
 #. TRANSLATORS: we keep a database updated with the time that an action was last executed
-#: ../client/pk-console.c:2063
+#: ../client/pk-console.c:2075
 msgid "Failed to get the time since this action was last completed"
 msgstr ""
 "Uzyskanie czasu od ostatniego zakończenia tego działania nie powiodło się"
 
 #. TRANSLATORS: The user did not provide a package name
 #. TRANSLATORS: This is when the user fails to supply the package name
-#: ../client/pk-console.c:2072 ../client/pk-console.c:2083
-#: ../client/pk-console.c:2091 ../client/pk-console.c:2107
-#: ../client/pk-console.c:2115 ../client/pk-generate-pack.c:241
+#: ../client/pk-console.c:2084 ../client/pk-console.c:2095
+#: ../client/pk-console.c:2103 ../client/pk-console.c:2119
+#: ../client/pk-console.c:2127 ../client/pk-generate-pack.c:241
 msgid "A package name is required"
 msgstr "Wymagana jest nazwa pakietu"
 
 #. TRANSLATORS: each package "provides" certain things, e.g. mime(gstreamer-decoder-mp3), the user didn't specify it
-#: ../client/pk-console.c:2099
+#: ../client/pk-console.c:2111
 msgid "A package provide string is required"
 msgstr "Wymagany jest łańcuch dostarczania pakietu"
 
 #. TRANSLATORS: The user didn't specify a filename to create as a list
-#: ../client/pk-console.c:2123
+#: ../client/pk-console.c:2135
 msgid "A list file name to create is required"
 msgstr "Wymagana jest lista nazw plików do utworzenia"
 
 #. TRANSLATORS: The user didn't specify a filename to open as a list
-#: ../client/pk-console.c:2132 ../client/pk-console.c:2141
+#: ../client/pk-console.c:2144 ../client/pk-console.c:2153
 msgid "A list file to open is required"
 msgstr "Wymagana jest lista plików do otwarcia"
 
 #. TRANSLATORS: The user tried to use an unsupported option on the command line
-#: ../client/pk-console.c:2194
+#: ../client/pk-console.c:2206
 #, c-format
 msgid "Option '%s' is not supported"
 msgstr "Opcja \"%s\" nie jest obsługiwana"
 
 #. TRANSLATORS: User does not have permission to do this
-#: ../client/pk-console.c:2207
+#: ../client/pk-console.c:2219
 msgid "Incorrect privileges for this operation"
 msgstr "Niepoprawne uprawnienia dla tego działania"
 
 #. TRANSLATORS: Generic failure of what they asked to do
-#: ../client/pk-console.c:2210
+#: ../client/pk-console.c:2222
 msgid "Command failed"
 msgstr "Polecenie nie powiodło się"
 
commit 1b88395a170da9942384f7ec7a1fc531dd67bc5b
Author: Richard Hughes <richard at hughsie.com>
Date:   Sun Jul 12 19:38:30 2009 +0100

    Fix a crash with the new code, where the signal wasn't being disconnected in one error case

diff --git a/src/gdb.sh b/src/gdb.sh
index c6072eb..31e6b99 100755
--- a/src/gdb.sh
+++ b/src/gdb.sh
@@ -1,4 +1,4 @@
 export G_DEBUG=fatal_criticals
 sudo touch /etc/PackageKit/PackageKit.conf
-sudo gdb --args .libs/lt-packagekitd --verbose --backend=dummy --disable-timer
+sudo gdb --args .libs/lt-packagekitd --verbose --backend=yum --disable-timer
 
diff --git a/src/pk-transaction-extra.c b/src/pk-transaction-extra.c
index 35d7807..fdeeef3 100644
--- a/src/pk-transaction-extra.c
+++ b/src/pk-transaction-extra.c
@@ -641,7 +641,7 @@ gboolean
 pk_transaction_extra_check_running_process (PkTransactionExtra *post, gchar **package_ids)
 {
 	PkStore *store;
-	guint signal_files;
+	guint signal_files = 0;
 
 	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
 
@@ -721,7 +721,7 @@ gboolean
 pk_transaction_extra_check_desktop_files (PkTransactionExtra *post, gchar **package_ids)
 {
 	PkStore *store;
-	guint signal_files;
+	guint signal_files = 0;
 
 	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
 
@@ -933,7 +933,7 @@ gboolean
 pk_transaction_extra_check_library_restart (PkTransactionExtra *post, gchar **package_ids)
 {
 	PkStore *store;
-	guint signal_files;
+	guint signal_files = 0;
 	gboolean ret = TRUE;
 	gchar **files = NULL;
 	GPtrArray *pids;
@@ -1000,10 +1000,10 @@ pk_transaction_extra_check_library_restart (PkTransactionExtra *post, gchar **pa
 	/* emit */
 	pk_transaction_extra_check_library_restart_emit (post, pids);
 	g_ptr_array_free (pids, TRUE);
-
-	g_signal_handler_disconnect (post->priv->backend, signal_files);
-	pk_transaction_extra_set_progress_changed (post, 100);
 out:
+	pk_transaction_extra_set_progress_changed (post, 100);
+	if (signal_files > 0)
+		g_signal_handler_disconnect (post->priv->backend, signal_files);
 	g_strfreev (files);
 	return ret;
 }
commit 176889ce5eaa097f7a4799b548dd60c82f565afc
Author: Richard Hughes <richard at hughsie.com>
Date:   Sun Jul 12 19:09:31 2009 +0100

    trivial: Relax the library checks a little to cope with lib64

diff --git a/src/pk-transaction-extra.c b/src/pk-transaction-extra.c
index 8bcc21b..35d7807 100644
--- a/src/pk-transaction-extra.c
+++ b/src/pk-transaction-extra.c
@@ -767,7 +767,7 @@ pk_transaction_extra_files_check_library_restart_cb (PkBackend *backend, const g
 	len = g_strv_length (files);
 	for (i=0; i<len; i++) {
 		/* not a system library */
-		if (strstr (files[i], "/lib/") == NULL)
+		if (strstr (files[i], "/lib") == NULL)
 			continue;
 
 		/* not a shared object */
commit 3c4eb8412bb1160f0515fc59452eaaba5570b0e2
Author: Richard Hughes <richard at hughsie.com>
Date:   Sun Jul 12 19:02:20 2009 +0100

    Add three new status enums to better describe what we are doing in the new code

diff --git a/lib/packagekit-glib/pk-enum.c b/lib/packagekit-glib/pk-enum.c
index bd47602..3a8a15e 100644
--- a/lib/packagekit-glib/pk-enum.c
+++ b/lib/packagekit-glib/pk-enum.c
@@ -87,6 +87,9 @@ static const PkEnumMatch enum_status[] = {
 	{PK_STATUS_ENUM_GENERATE_PACKAGE_LIST,	"generate-package-list"},
 	{PK_STATUS_ENUM_WAITING_FOR_LOCK,	"waiting-for-lock"},
 	{PK_STATUS_ENUM_WAITING_FOR_AUTH,	"waiting-for-auth"},
+	{PK_STATUS_ENUM_SCAN_PROCESS_LIST,	"scan-process-list"},
+	{PK_STATUS_ENUM_CHECK_EXECUTABLE_FILES,	"check-executable-files"},
+	{PK_STATUS_ENUM_CHECK_LIBRARIES,	"check-libraries"},
 	{0, NULL}
 };
 
diff --git a/lib/packagekit-glib/pk-enum.h b/lib/packagekit-glib/pk-enum.h
index 927a9dd..6ccf65f 100644
--- a/lib/packagekit-glib/pk-enum.h
+++ b/lib/packagekit-glib/pk-enum.h
@@ -136,6 +136,9 @@ typedef enum {
 	PK_STATUS_ENUM_GENERATE_PACKAGE_LIST,
 	PK_STATUS_ENUM_WAITING_FOR_LOCK,
 	PK_STATUS_ENUM_WAITING_FOR_AUTH,
+	PK_STATUS_ENUM_SCAN_PROCESS_LIST,
+	PK_STATUS_ENUM_CHECK_EXECUTABLE_FILES,
+	PK_STATUS_ENUM_CHECK_LIBRARIES,
 	PK_STATUS_ENUM_UNKNOWN
 } PkStatusEnum;
 
diff --git a/src/pk-transaction-extra.c b/src/pk-transaction-extra.c
index 740ba8a..8bcc21b 100644
--- a/src/pk-transaction-extra.c
+++ b/src/pk-transaction-extra.c
@@ -650,7 +650,7 @@ pk_transaction_extra_check_running_process (PkTransactionExtra *post, gchar **pa
 		return FALSE;
 	}
 
-	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_CHECK_EXECUTABLE_FILES);
 	pk_transaction_extra_set_progress_changed (post, 101);
 
 	store = pk_backend_get_store (post->priv->backend);
@@ -949,6 +949,10 @@ pk_transaction_extra_check_library_restart (PkTransactionExtra *post, gchar **pa
 	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
 	g_ptr_array_set_size (post->priv->files_list, 0);
 
+	/* set status */
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_PROCESS_LIST);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
 	/* get list from lsof */
 	ret = pk_lsof_refresh (post->priv->lsof);
 	if (!ret) {
@@ -956,8 +960,8 @@ pk_transaction_extra_check_library_restart (PkTransactionExtra *post, gchar **pa
 		goto out;
 	}
 
-	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
-	pk_transaction_extra_set_progress_changed (post, 101);
+	/* set status */
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_CHECK_LIBRARIES);
 
 	store = pk_backend_get_store (post->priv->backend);
 	signal_files = g_signal_connect (post->priv->backend, "files",
commit 5086f3a6fd979d68e3327afc25b69a86e19f1c7e
Author: Mounir Lamouri (volkmar) <mounir.lamouri at gmail.com>
Date:   Sun Jul 12 14:57:39 2009 +0200

    portage: update get-requires to let it works with svn portage version

diff --git a/backends/portage/portageBackend.py b/backends/portage/portageBackend.py
index ace0e60..51c0fe1 100755
--- a/backends/portage/portageBackend.py
+++ b/backends/portage/portageBackend.py
@@ -753,11 +753,13 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
         myopts = {}
         spinner = ""
         favorites = []
-        settings, trees, mtimedb = _emerge.load_emerge_config()
-        spinner = _emerge.stdout_spinner()
-        rootconfig = _emerge.RootConfig(self.portage_settings, trees["/"],
-                portage._sets.load_default_config(
-                    self.portage_settings, trees["/"]))
+
+        spinner = _emerge.stdout_spinner.stdout_spinner()
+        settings, trees, _ = _emerge.actions.load_emerge_config()
+        rootconfig = _emerge.RootConfig.RootConfig(
+                self.portage_settings, trees["/"],
+                portage.sets.load_default_config(self.portage_settings,
+                    trees["/"]))
 
         for pkg in pkgs:
             cpv = id_to_cpv(pkg)
@@ -774,7 +776,7 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
             required_set_names = ("system", "world")
             required_sets = {}
 
-            args_set = portage._sets.base.InternalPackageSet()
+            args_set = portage.sets.base.InternalPackageSet()
             args_set.update(["="+cpv]) # parameters is converted to atom
             # or use portage.dep_expand
 
@@ -783,12 +785,13 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
                         "Was not able to generate atoms")
                 continue
             
-            depgraph = _emerge.depgraph(settings, trees, myopts,
-                    _emerge.create_depgraph_params(myopts, "remove"), spinner)
-            vardb = depgraph.trees["/"]["vartree"].dbapi
+            depgraph = _emerge.depgraph.depgraph(settings, trees, myopts,
+                    _emerge.create_depgraph_params.create_depgraph_params(
+                        myopts, "remove"), spinner)
+            vardb = depgraph._frozen_config.trees["/"]["vartree"].dbapi
 
             for s in required_set_names:
-                required_sets[s] = portage._sets.base.InternalPackageSet(
+                required_sets[s] = portage.sets.base.InternalPackageSet(
                         initial_atoms=rootconfig.setconfig.getSetAtoms(s))
 
             # TODO: error/warning if world = null or system = null ?
@@ -796,7 +799,6 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
             # TODO: not sure it's needed. for deselect in emerge...
             required_sets["world"].clear()
             for pkg in vardb:
-                spinner.update()
                 try:
                     if args_set.findAtomForPackage(pkg) is None:
                         required_sets["world"].add("=" + pkg.cpv)
@@ -805,15 +807,15 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
 
             set_args = {}
             for s, pkg_set in required_sets.iteritems():
-                set_atom = portage._sets.SETPREFIX + s
-                set_arg = _emerge.SetArg(arg=set_atom, set=pkg_set,
-                        root_config=depgraph.roots[portage.settings["ROOT"]])
+                set_atom = portage.sets.SETPREFIX + s
+                set_arg = _emerge.SetArg.SetArg(arg=set_atom, set=pkg_set,
+                        root_config=depgraph._frozen_config.roots[portage.settings["ROOT"]])
                 set_args[s] = set_arg
                 for atom in set_arg.set:
-                    depgraph._dep_stack.append(
-                            _emerge.Dependency(atom=atom, root=portage.settings["ROOT"],
+                    depgraph._dynamic_config._dep_stack.append(
+                            _emerge.Dependency.Dependency(atom=atom, root=portage.settings["ROOT"],
                                 parent=set_arg))
-                    depgraph.digraph.add(set_arg, None)
+                    depgraph._dynamic_config.digraph.add(set_arg, None)
 
             if not depgraph._complete_graph():
                 self.error(ERROR_INTERNAL_ERROR, "Error when generating depgraph")
@@ -835,8 +837,8 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
                 except portage.exception.InvalidDependString:
                     continue
 
-                if arg_atom and pkg in depgraph.digraph:
-                    parents = depgraph.digraph.parent_nodes(pkg)
+                if arg_atom and pkg in depgraph._dynamic_config.digraph:
+                    parents = depgraph._dynamic_config.digraph.parent_nodes(pkg)
                     for node in parents:
                         self.package(node[2])
 
commit bf915c29df209e9dce55f9b5c154eccb13388e54
Author: Mounir Lamouri (volkmar) <mounir.lamouri at gmail.com>
Date:   Sun Jul 12 12:59:52 2009 +0200

    portage: get-depends filtering output, get-requires manages ERROR_CANNOT_GET_REQUIRES, comments and cosmetic

diff --git a/backends/portage/portageBackend.py b/backends/portage/portageBackend.py
index dab904b..ace0e60 100755
--- a/backends/portage/portageBackend.py
+++ b/backends/portage/portageBackend.py
@@ -527,7 +527,8 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
 
     def get_depends(self, filters, pkgs, recursive):
         # TODO: use only myparams ?
-        # TODO: error management
+        # TODO: improve error management / info
+        # TODO: show DEPEND-only depends too
 
         # FILTERS:
         # - installed: ok
@@ -548,7 +549,7 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
             if not self.is_cpv_valid(cpv):
                 self.error(ERROR_PACKAGE_NOT_FOUND,
                         "Package %s was not found" % pkg)
-                return
+                continue
             cpv_input.append('=' + cpv)
 
         myopts = {}
@@ -558,11 +559,16 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
         settings, trees, _ = _emerge.actions.load_emerge_config()
         myparams = _emerge.create_depgraph_params.create_depgraph_params(
                 myopts, "")
-        depgraph = _emerge.depgraph.depgraph(
-                settings, trees, myopts, myparams, spinner)
-        retval, fav = depgraph.select_files(cpv_input)
+
+        try:
+            self.block_output()
+            depgraph = _emerge.depgraph.depgraph(
+                    settings, trees, myopts, myparams, spinner)
+            retval, fav = depgraph.select_files(cpv_input)
+        finally:
+            self.unblock_output()
+
         if not retval:
-            depgraph.display_problems()
             self.error(ERROR_INTERNAL_ERROR,
                     "Wasn't able to get dependency graph")
             return
@@ -740,7 +746,6 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
         # TODO: filters
         # TODO: recursive not implemented
         # TODO: usefulness ? use cases
-        # TODO: work only on installed packages
         self.status(STATUS_RUNNING)
         self.allow_cancel(True)
         self.percentage(None)
@@ -751,16 +756,19 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
         settings, trees, mtimedb = _emerge.load_emerge_config()
         spinner = _emerge.stdout_spinner()
         rootconfig = _emerge.RootConfig(self.portage_settings, trees["/"],
-                portage._sets.load_default_config(self.portage_settings, trees["/"]))
+                portage._sets.load_default_config(
+                    self.portage_settings, trees["/"]))
 
         for pkg in pkgs:
             cpv = id_to_cpv(pkg)
 
-            # is cpv installed
-            # TODO: keep error msg ?
-            if not self.vardb.match(cpv):
-                self.error(ERROR_PACKAGE_NOT_INSTALLED,
-                        "Package %s is not installed" % pkg)
+            if not self.is_cpv_valid(cpv):
+                self.error(ERROR_PACKAGE_NOT_FOUND,
+                        "Package %s was not found" % pkg)
+                continue
+            if not self.is_installed(cpv):
+                self.error(ERROR_CANNOT_GET_REQUIRES,
+                        "get-requires is only available for installed packages")
                 continue
 
             required_set_names = ("system", "world")
@@ -771,7 +779,8 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
             # or use portage.dep_expand
 
             if not args_set:
-                self.error(ERROR_INTERNAL_ERROR, "Was not able to generate atoms")
+                self.error(ERROR_INTERNAL_ERROR,
+                        "Was not able to generate atoms")
                 continue
             
             depgraph = _emerge.depgraph(settings, trees, myopts,
commit cdc781e22d9984cff6f5d07648e471c1360c92ee
Author: Mounir Lamouri (volkmar) <mounir.lamouri at gmail.com>
Date:   Sun Jul 12 01:24:11 2009 +0200

    portage: improve get-depends and manage filters

diff --git a/backends/portage/portageBackend.py b/backends/portage/portageBackend.py
index 223fe81..dab904b 100755
--- a/backends/portage/portageBackend.py
+++ b/backends/portage/portageBackend.py
@@ -30,6 +30,7 @@ import portage
 import _emerge.actions
 import _emerge.stdout_spinner
 import _emerge.create_depgraph_params
+import _emerge.AtomArg
 
 # layman imports
 import layman.db
@@ -525,53 +526,97 @@ class PackageKitPortageBackend(PackageKitBaseBackend, PackagekitPackage):
         PackageKitBaseBackend.package(self, self.cpv_to_id(cpv), info, desc)
 
     def get_depends(self, filters, pkgs, recursive):
-        # TODO: manage filters
-        # TODO: optimize by using vardb for installed packages ?
-        # TODO: use depgraph.select_files to select multiple files
-        # TODO: print package in depends ?
+        # TODO: use only myparams ?
+        # TODO: error management
+
+        # FILTERS:
+        # - installed: ok
+        # - free: ok
+        # - newest: ignored because only one version of a package is installed
+
         self.status(STATUS_INFO)
         self.allow_cancel(True)
         self.percentage(None)
 
+        fltlist = filters.split(';')
+
+        cpv_input = []
+        cpv_list = []
+
         for pkg in pkgs:
             cpv = id_to_cpv(pkg)
-
             if not self.is_cpv_valid(cpv):
                 self.error(ERROR_PACKAGE_NOT_FOUND,
                         "Package %s was not found" % pkg)
-                continue
+                return
+            cpv_input.append('=' + cpv)
 
-            myopts = {}
-            if recursive:
-                myopts["--emptytree"] = True
-            spinner = _emerge.stdout_spinner.stdout_spinner()
-            settings, trees, _ = _emerge.actions.load_emerge_config()
-            myparams = _emerge.create_depgraph_params.create_depgraph_params(
-                    myopts, "")
-            depgraph = _emerge.depgraph.depgraph(
-                    settings, trees, myopts, myparams, spinner)
-
-            retval, fav = depgraph.select_files(["="+cpv])
-            if not retval:
-                self.error(ERROR_INTERNAL_ERROR,
-                        "Wasn't able to get dependency graph")
-                continue
+        myopts = {}
+        myopts["--selective"] = True
+        myopts["--deep"] = True
+        spinner = _emerge.stdout_spinner.stdout_spinner()
+        settings, trees, _ = _emerge.actions.load_emerge_config()
+        myparams = _emerge.create_depgraph_params.create_depgraph_params(
+                myopts, "")
+        depgraph = _emerge.depgraph.depgraph(
+                settings, trees, myopts, myparams, spinner)
+        retval, fav = depgraph.select_files(cpv_input)
+        if not retval:
+            depgraph.display_problems()
+            self.error(ERROR_INTERNAL_ERROR,
+                    "Wasn't able to get dependency graph")
+            return
+
+        def _add_children_to_list(cpv_list, node):
+            for n in depgraph._dynamic_config.digraph.child_nodes(node):
+                if n not in cpv_list:
+                    cpv_list.append(n)
+                    _add_children_to_list(cpv_list, n)
+
+        for cpv in cpv_input:
+            for r in depgraph._dynamic_config.digraph.root_nodes():
+                if not isinstance(r, _emerge.AtomArg.AtomArg):
+                    continue
+                if r.atom == cpv:
+                    if recursive:
+                        _add_children_to_list(cpv_list, r)
+                    else:
+                        for n in \
+                                depgraph._dynamic_config.digraph.child_nodes(r):
+                            for c in \
+                                depgraph._dynamic_config.digraph.child_nodes(n):
+                                cpv_list.append(c)
+
+        def _filter_uninstall(cpv):
+            return cpv[3] != 'uninstall'
+        def _filter_installed(cpv):
+            return cpv[0] == 'installed'
+        def _filter_not_installed(cpv):
+            return cpv[0] != 'installed'
+
+        # removing packages going to be uninstalled
+        cpv_list = filter(_filter_uninstall, cpv_list)
+
+        # install filter
+        if FILTER_INSTALLED in fltlist:
+            cpv_list = filter(_filter_installed, cpv_list)
+        if FILTER_NOT_INSTALLED in fltlist:
+            cpv_list = filter(_filter_not_installed, cpv_list)
 
-            if recursive:
-                # printing the whole tree
-                for p in depgraph.altlist(reversed=1):
-                    if p[2] != cpv:
-                        self.package(p[2])
-            else: # !recursive
-                # only printing child of the root node
-                # actually, we have "=cpv" -> "cpv" -> children
-                root_node = depgraph._dynamic_config.digraph.root_nodes()[0] # =cpv
-                root_node = depgraph._dynamic_config.digraph.child_nodes(
-                        root_node)[0] # cpv
-                children = depgraph._dynamic_config.digraph.child_nodes(
-                        root_node)
-                for child in children:
-                    self.package(child[2])
+        # now we can change cpv_list to a real cpv list
+        tmp_list = cpv_list[:]
+        cpv_list = []
+        for x in tmp_list:
+            cpv_list.append(x[2])
+        del tmp_list
+
+        # free filter
+        cpv_list = self.filter_free(cpv_list, fltlist)
+
+        for cpv in cpv_list:
+            # prevent showing input packages
+            if '=' + cpv not in cpv_input:
+                self.package(cpv)
 
     def get_details(self, pkgs):
         self.status(STATUS_INFO)
commit 52961029319579df9d20ab3a4f203f8d091bf99f
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 17:56:34 2009 +0100

    One less FIXME, check the install-package for security updates

diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index 0aa4e2a..697fbc6 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -1060,26 +1060,26 @@ pk_transaction_update_detail_cb (PkBackend *backend, const PkUpdateDetailObj *de
 
 /**
  * pk_transaction_pre_transaction_checks:
- *
- * TODO: also check if package in InstallPackages is in the update lists
- *       and do the same check if this is so.
  */
 static gboolean
-pk_transaction_pre_transaction_checks (PkTransaction *transaction)
+pk_transaction_pre_transaction_checks (PkTransaction *transaction, gchar **package_ids)
 {
 	PkPackageList *updates;
 	const PkPackageObj *obj;
 	guint i;
+	guint j;
 	guint length;
 	gboolean ret = FALSE;
 	gchar *package_id;
-	gchar **package_ids = NULL;
+	gchar **package_ids_security = NULL;
 	GPtrArray *list = NULL;
+	const gchar *package_id_tmp;
 
 	/* only do this for update actions */
 	if (transaction->priv->role != PK_ROLE_ENUM_UPDATE_SYSTEM &&
-	    transaction->priv->role != PK_ROLE_ENUM_UPDATE_PACKAGES) {
-		egg_debug ("doing nothing, as not update");
+	    transaction->priv->role != PK_ROLE_ENUM_UPDATE_PACKAGES &&
+	    transaction->priv->role != PK_ROLE_ENUM_INSTALL_PACKAGES) {
+		egg_debug ("doing nothing, as not update or install");
 		goto out;
 	}
 
@@ -1109,11 +1109,33 @@ pk_transaction_pre_transaction_checks (PkTransaction *transaction)
 		}
 	}
 
+	/* is a security update we are installing */
+	if (transaction->priv->role == PK_ROLE_ENUM_INSTALL_PACKAGES) {
+		ret = FALSE;
+
+		/* do any of the packages we are updating match */
+		for (i=0; i < list->len; i++) {
+			package_id_tmp = g_ptr_array_index (list, i);
+			for (j=0; package_ids[j] != NULL; j++) {
+				if (g_strcmp0 (package_id_tmp, package_ids[j]) == 0) {
+					ret = TRUE;
+					break;
+				}
+			}
+		}
+
+		/* nothing matched */
+		if (!ret) {
+			egg_debug ("not installing a security update package");
+			goto out;
+		}
+	}
+
 	/* find files in security updates */
-	package_ids = pk_package_ids_from_array (list);
-	ret = pk_transaction_extra_check_library_restart (transaction->priv->transaction_extra, package_ids);
+	package_ids_security = pk_package_ids_from_array (list);
+	ret = pk_transaction_extra_check_library_restart (transaction->priv->transaction_extra, package_ids_security);
 out:
-	g_strfreev (package_ids);
+	g_strfreev (package_ids_security);
 	if (list != NULL) {
 		g_ptr_array_foreach (list, (GFunc) g_free, NULL);
 		g_ptr_array_free (list, TRUE);
@@ -1157,7 +1179,7 @@ pk_transaction_set_running (PkTransaction *transaction)
 	egg_debug ("setting role for %s to %s", priv->tid, pk_role_enum_to_text (priv->role));
 
 	/* do any pre transaction checks */
-	ret = pk_transaction_pre_transaction_checks (transaction);
+	ret = pk_transaction_pre_transaction_checks (transaction, priv->cached_package_ids);
 
 	/* might have to reset again if we used the backend */
 	if (ret)
commit 6e88599849d09fcd6fb08b6e86bfc1ea410d63a0
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 17:11:41 2009 +0100

    Rename PkPostTrans to PkTransactionExtra as we're doing operations before the transactions now too

diff --git a/src/Makefile.am b/src/Makefile.am
index 6ada872..9a48b12 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -55,6 +55,8 @@ shared_SOURCES =					\
 	pk-lsof.h					\
 	pk-transaction.c				\
 	pk-transaction.h				\
+	pk-transaction-extra.c				\
+	pk-transaction-extra.h				\
 	pk-backend.c					\
 	pk-backend.h					\
 	pk-backend-internal.h				\
@@ -62,8 +64,6 @@ shared_SOURCES =					\
 	pk-network.h					\
 	pk-shared.c					\
 	pk-shared.h					\
-	pk-post-trans.c					\
-	pk-post-trans.h					\
 	pk-time.h					\
 	pk-time.c					\
 	pk-network-stack.h				\
diff --git a/src/pk-post-trans.c b/src/pk-post-trans.c
deleted file mode 100644
index 3021ff1..0000000
--- a/src/pk-post-trans.c
+++ /dev/null
@@ -1,1163 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 Richard Hughes <richard at hughsie.com>
- *
- * Licensed under the GNU General Public License Version 2
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifdef HAVE_CONFIG_H
-#  include <config.h>
-#endif
-
-#include <string.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <glib.h>
-#include <glib/gi18n.h>
-#include <packagekit-glib/packagekit.h>
-#include <gio/gdesktopappinfo.h>
-#include <sqlite3.h>
-
-#include "egg-debug.h"
-
-#include "pk-post-trans.h"
-#include "pk-shared.h"
-#include "pk-marshal.h"
-#include "pk-backend-internal.h"
-#include "pk-lsof.h"
-
-#define PK_POST_TRANS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_POST_TRANS, PkPostTransPrivate))
-
-struct PkPostTransPrivate
-{
-	sqlite3			*db;
-	PkBackend		*backend;
-	GMainLoop		*loop;
-	PkObjList		*running_exec_list;
-	PkPackageList		*list;
-	PkLsof			*lsof;
-	guint			 finished_id;
-	guint			 package_id;
-	GHashTable		*hash;
-	GPtrArray		*files_list;
-};
-
-enum {
-	PK_POST_TRANS_STATUS_CHANGED,
-	PK_POST_TRANS_PROGRESS_CHANGED,
-	PK_POST_TRANS_REQUIRE_RESTART,
-	PK_POST_TRANS_LAST_SIGNAL
-};
-
-static guint signals [PK_POST_TRANS_LAST_SIGNAL] = { 0 };
-G_DEFINE_TYPE (PkPostTrans, pk_post_trans, G_TYPE_OBJECT)
-
-/**
- * pk_post_trans_finished_cb:
- **/
-static void
-pk_post_trans_finished_cb (PkBackend *backend, PkExitEnum exit_enum, PkPostTrans *post)
-{
-	if (g_main_loop_is_running (post->priv->loop))
-		g_main_loop_quit (post->priv->loop);
-}
-
-/**
- * pk_post_trans_package_cb:
- **/
-static void
-pk_post_trans_package_cb (PkBackend *backend, const PkPackageObj *obj, PkPostTrans *post)
-{
-	pk_obj_list_add (PK_OBJ_LIST(post->priv->list), obj);
-}
-
-/**
- * pk_post_trans_set_require_restart:
- **/
-static void
-pk_post_trans_set_require_restart (PkPostTrans *post, PkRestartEnum restart, const gchar *package_id)
-{
-	egg_debug ("emit require-restart %s, %s", pk_restart_enum_to_text (restart), package_id);
-	g_signal_emit (post, signals [PK_POST_TRANS_REQUIRE_RESTART], 0, restart, package_id);
-}
-
-/**
- * pk_post_trans_set_status_changed:
- **/
-static void
-pk_post_trans_set_status_changed (PkPostTrans *post, PkStatusEnum status)
-{
-	egg_debug ("emiting status-changed %s", pk_status_enum_to_text (status));
-	g_signal_emit (post, signals [PK_POST_TRANS_STATUS_CHANGED], 0, status);
-}
-
-/**
- * pk_post_trans_set_progress_changed:
- **/
-static void
-pk_post_trans_set_progress_changed (PkPostTrans *post, guint percentage)
-{
-	egg_debug ("emiting progress-changed %i", percentage);
-	g_signal_emit (post, signals [PK_POST_TRANS_PROGRESS_CHANGED], 0, percentage, 0, 0, 0);
-}
-
-/**
- * pk_post_trans_get_installed_package_for_file:
- **/
-static const PkPackageObj *
-pk_post_trans_get_installed_package_for_file (PkPostTrans *post, const gchar *filename)
-{
-	guint size;
-	const PkPackageObj *obj = NULL;
-	PkStore *store;
-
-	/* use PK to find the correct package */
-	pk_obj_list_clear (PK_OBJ_LIST(post->priv->list));
-	pk_backend_reset (post->priv->backend);
-	store = pk_backend_get_store (post->priv->backend);
-	pk_store_set_uint (store, "filters", pk_bitfield_value (PK_FILTER_ENUM_INSTALLED));
-	pk_store_set_string (store, "search", filename);
-	post->priv->backend->desc->search_file (post->priv->backend, pk_bitfield_value (PK_FILTER_ENUM_INSTALLED), filename);
-
-	/* wait for finished */
-	g_main_loop_run (post->priv->loop);
-
-	/* check that we only matched one package */
-	size = pk_package_list_get_size (post->priv->list);
-	if (size != 1) {
-		egg_warning ("not correct size, %i", size);
-		goto out;
-	}
-
-	/* get the obj */
-	obj = pk_package_list_get_obj (post->priv->list, 0);
-	if (obj == NULL) {
-		egg_warning ("cannot get obj");
-		goto out;
-	}
-out:
-	return obj;
-}
-
-/**
- * pk_post_trans_string_list_new:
- **/
-static PkObjList *
-pk_post_trans_string_list_new ()
-{
-	PkObjList *list;
-	list = pk_obj_list_new ();
-	pk_obj_list_set_compare (list, (PkObjListCompareFunc) g_strcmp0);
-	pk_obj_list_set_copy (list, (PkObjListCopyFunc) g_strdup);
-	pk_obj_list_set_free (list, (PkObjListFreeFunc) g_free);
-	pk_obj_list_set_to_string (list, (PkObjListToStringFunc) g_strdup);
-	pk_obj_list_set_from_string (list, (PkObjListFromStringFunc) g_strdup);
-	return list;
-}
-
-/**
- * pk_post_trans_get_filename_md5:
- **/
-static gchar *
-pk_post_trans_get_filename_md5 (const gchar *filename)
-{
-	gchar *md5 = NULL;
-	gchar *data = NULL;
-	gsize length;
-	GError *error = NULL;
-	gboolean ret;
-
-	/* check is no longer exists */
-	ret = g_file_test (filename, G_FILE_TEST_EXISTS);
-	if (!ret)
-		goto out;
-
-	/* get data */
-	ret = g_file_get_contents (filename, &data, &length, &error);
-	if (!ret) {
-		egg_warning ("failed to open file %s: %s", filename, error->message);
-		g_error_free (error);
-		goto out;
-	}
-
-	/* check md5 is same */
-	md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) data, length);
-out:
-	g_free (data);
-	return md5;
-}
-
-/**
- * pk_post_trans_sqlite_remove_filename:
- **/
-static gint
-pk_post_trans_sqlite_remove_filename (PkPostTrans *post, const gchar *filename)
-{
-	gchar *statement;
-	gint rc;
-
-	statement = g_strdup_printf ("DELETE FROM cache WHERE filename = '%s'", filename);
-	rc = sqlite3_exec (post->priv->db, statement, NULL, NULL, NULL);
-	g_free (statement);
-	return rc;
-}
-
-/**
- * pk_post_trans_sqlite_add_filename_details:
- **/
-static gint
-pk_post_trans_sqlite_add_filename_details (PkPostTrans *post, const gchar *filename, const gchar *package, const gchar *md5)
-{
-	gchar *statement;
-	gchar *error_msg = NULL;
-	sqlite3_stmt *sql_statement = NULL;
-	gint rc = -1;
-	gint show;
-	GDesktopAppInfo *info;
-
-	/* find out if we should show desktop file in menus */
-	info = g_desktop_app_info_new_from_filename (filename);
-	if (info == NULL) {
-		egg_warning ("could not load desktop file %s", filename);
-		goto out;
-	}
-	show = g_app_info_should_show (G_APP_INFO (info));
-	g_object_unref (info);
-
-	egg_debug ("add filename %s from %s with md5: %s (show: %i)", filename, package, md5, show);
-
-	/* the row might already exist */
-	statement = g_strdup_printf ("DELETE FROM cache WHERE filename = '%s'", filename);
-	sqlite3_exec (post->priv->db, statement, NULL, NULL, NULL);
-	g_free (statement);
-
-	/* prepare the query, as we don't escape it */
-	rc = sqlite3_prepare_v2 (post->priv->db, "INSERT INTO cache (filename, package, show, md5) VALUES (?, ?, ?, ?)", -1, &sql_statement, NULL);
-	if (rc != SQLITE_OK) {
-		egg_warning ("SQL failed to prepare: %s", sqlite3_errmsg (post->priv->db));
-		goto out;
-	}
-
-	/* add data */
-	sqlite3_bind_text (sql_statement, 1, filename, -1, SQLITE_STATIC);
-	sqlite3_bind_text (sql_statement, 2, package, -1, SQLITE_STATIC);
-	sqlite3_bind_int (sql_statement, 3, show);
-	sqlite3_bind_text (sql_statement, 4, md5, -1, SQLITE_STATIC);
-
-	/* save this */
-	sqlite3_step (sql_statement);
-	rc = sqlite3_finalize (sql_statement);
-	if (rc != SQLITE_OK) {
-		egg_warning ("SQL error: %s\n", error_msg);
-		sqlite3_free (error_msg);
-		goto out;
-	}
-
-out:
-	return rc;
-}
-
-/**
- * pk_post_trans_sqlite_add_filename:
- **/
-static gint
-pk_post_trans_sqlite_add_filename (PkPostTrans *post, const gchar *filename, const gchar *md5_opt)
-{
-	gchar *md5 = NULL;
-	gchar *package = NULL;
-	gint rc = -1;
-	const PkPackageObj *obj;
-
-	/* if we've got it, use old data */
-	if (md5_opt != NULL)
-		md5 = g_strdup (md5_opt);
-	else
-		md5 = pk_post_trans_get_filename_md5 (filename);
-
-	/* resolve */
-	obj = pk_post_trans_get_installed_package_for_file (post, filename);
-	if (obj == NULL) {
-		egg_warning ("failed to get list");
-		goto out;
-	}
-
-	/* add */
-	rc = pk_post_trans_sqlite_add_filename_details (post, filename, obj->id->name, md5);
-out:
-	g_free (md5);
-	g_free (package);
-	return rc;
-}
-
-/**
- * pk_post_trans_sqlite_cache_rescan_cb:
- **/
-static gint
-pk_post_trans_sqlite_cache_rescan_cb (void *data, gint argc, gchar **argv, gchar **col_name)
-{
-	PkPostTrans *post = PK_POST_TRANS (data);
-	const gchar *filename = NULL;
-	const gchar *md5 = NULL;
-	gchar *md5_calc = NULL;
-	gint i;
-
-	/* add the filename data to the array */
-	for (i=0; i<argc; i++) {
-		if (g_strcmp0 (col_name[i], "filename") == 0 && argv[i] != NULL)
-			filename = argv[i];
-		else if (g_strcmp0 (col_name[i], "md5") == 0 && argv[i] != NULL)
-			md5 = argv[i];
-	}
-
-	/* sanity check */
-	if (filename == NULL || md5 == NULL) {
-		egg_warning ("filename %s and md5 %s)", filename, md5);
-		goto out;
-	}
-
-	/* get md5 */
-	md5_calc = pk_post_trans_get_filename_md5 (filename);
-	if (md5_calc == NULL) {
-		egg_debug ("remove of %s as no longer found", filename);
-		pk_post_trans_sqlite_remove_filename (post, filename);
-		goto out;
-	}
-
-	/* we've checked the file */
-	g_hash_table_insert (post->priv->hash, g_strdup (filename), GUINT_TO_POINTER (1));
-
-	/* check md5 is same */
-	if (g_strcmp0 (md5, md5_calc) != 0) {
-		egg_debug ("add of %s as md5 invalid (%s vs %s)", filename, md5, md5_calc);
-		pk_post_trans_sqlite_add_filename (post, filename, md5_calc);
-	}
-
-	egg_debug ("existing filename %s valid, md5=%s", filename, md5);
-out:
-	g_free (md5_calc);
-	return 0;
-}
-
-/**
- * pk_post_trans_import_desktop_files:
- **/
-gboolean
-pk_post_trans_import_desktop_files (PkPostTrans *post)
-{
-	gchar *statement;
-	gchar *error_msg = NULL;
-	gint rc;
-	GError *error = NULL;
-	GDir *dir;
-	const gchar *filename;
-	gpointer data;
-	gchar *path;
-	GPtrArray *array;
-	gfloat step;
-	guint i;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-	g_return_val_if_fail (post->priv->db != NULL, FALSE);
-
-	if (post->priv->backend->desc->search_file == NULL) {
-		egg_debug ("cannot search files");
-		return FALSE;
-	}
-
-	/* use a local backend instance */
-	pk_backend_reset (post->priv->backend);
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
-
-	/* reset hash */
-	g_hash_table_remove_all (post->priv->hash);
-	pk_post_trans_set_progress_changed (post, 101);
-
-	/* first go through the existing data, and look for modifications and removals */
-	statement = g_strdup ("SELECT filename, md5 FROM cache");
-	rc = sqlite3_exec (post->priv->db, statement, pk_post_trans_sqlite_cache_rescan_cb, post, &error_msg);
-	g_free (statement);
-	if (rc != SQLITE_OK) {
-		egg_warning ("SQL error: %s\n", error_msg);
-		sqlite3_free (error_msg);
-	}
-
-	/* open directory */
-	dir = g_dir_open (PK_DESKTOP_DEFAULT_APPLICATION_DIR, 0, &error);
-	if (dir == NULL) {
-		egg_warning ("failed to open file %s: %s", PK_DESKTOP_DEFAULT_APPLICATION_DIR, error->message);
-		g_error_free (error);
-		goto out;
-	}
-
-	/* go through desktop files and add them to an array if not present */
-	filename = g_dir_read_name (dir);
-	array = g_ptr_array_new ();
-	while (filename != NULL) {
-		if (g_str_has_suffix (filename, ".desktop")) {
-			path = g_build_filename (PK_DESKTOP_DEFAULT_APPLICATION_DIR, filename, NULL);
-			data = g_hash_table_lookup (post->priv->hash, path);
-			if (data == NULL) {
-				egg_debug ("add of %s as not present in db", path);
-				g_ptr_array_add (array, g_strdup (path));
-			}
-			g_free (path);
-		}
-		filename = g_dir_read_name (dir);
-	}
-	g_dir_close (dir);
-
-	step = 100.0f / array->len;
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST);
-
-	/* process files in an array */
-	for (i=0; i<array->len; i++) {
-		pk_post_trans_set_progress_changed (post, i * step);
-		path = g_ptr_array_index (array, i);
-		pk_post_trans_sqlite_add_filename (post, path, NULL);
-	}
-	g_ptr_array_foreach (array, (GFunc) g_free, NULL);
-	g_ptr_array_free (array, TRUE);
-
-out:
-	pk_post_trans_set_progress_changed (post, 100);
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_FINISHED);
-	return TRUE;
-}
-
-/**
- * pk_post_trans_update_package_list:
- **/
-gboolean
-pk_post_trans_update_package_list (PkPostTrans *post)
-{
-	gboolean ret;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-
-	if (post->priv->backend->desc->get_packages == NULL) {
-		egg_debug ("cannot get packages");
-		return FALSE;
-	}
-
-	egg_debug ("updating package lists");
-
-	/* clear old list */
-	pk_obj_list_clear (PK_OBJ_LIST(post->priv->list));
-
-	/* update UI */
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST);
-	pk_post_trans_set_progress_changed (post, 101);
-
-	/* get the new package list */
-	pk_backend_reset (post->priv->backend);
-	pk_store_set_uint (pk_backend_get_store (post->priv->backend), "filters", pk_bitfield_value (PK_FILTER_ENUM_NONE));
-	post->priv->backend->desc->get_packages (post->priv->backend, PK_FILTER_ENUM_NONE);
-
-	/* wait for finished */
-	g_main_loop_run (post->priv->loop);
-
-	/* update UI */
-	pk_post_trans_set_progress_changed (post, 90);
-
-	/* convert to a file */
-	ret = pk_obj_list_to_file (PK_OBJ_LIST(post->priv->list), PK_SYSTEM_PACKAGE_LIST_FILENAME);
-	if (!ret)
-		egg_warning ("failed to save to file");
-
-	/* update UI */
-	pk_post_trans_set_progress_changed (post, 100);
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_FINISHED);
-
-	return ret;
-}
-
-/**
- * pk_post_trans_clear_firmware_requests:
- **/
-gboolean
-pk_post_trans_clear_firmware_requests (PkPostTrans *post)
-{
-	gboolean ret;
-	gchar *filename;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-
-	/* clear the firmware requests directory */
-	filename = g_build_filename (LOCALSTATEDIR, "run", "PackageKit", "udev", NULL);
-	egg_debug ("clearing udev firmware requests at %s", filename);
-	ret = pk_directory_remove_contents (filename);
-	if (!ret)
-		egg_warning ("failed to clear %s", filename);
-	g_free (filename);
-	return ret;
-}
-
-
-/**
- * pk_post_trans_update_files_check_running_cb:
- **/
-static void
-pk_post_trans_update_files_check_running_cb (PkBackend *backend, const gchar *package_id,
-					     const gchar *filelist, PkPostTrans *post)
-{
-	guint i;
-	guint len;
-	gboolean ret;
-	gchar **files;
-	PkPackageId *id;
-
-	id = pk_package_id_new_from_string (package_id);
-	files = g_strsplit (filelist, ";", 0);
-
-	/* check each file */
-	len = g_strv_length (files);
-	for (i=0; i<len; i++) {
-		/* executable? */
-		ret = g_file_test (files[i], G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_EXISTS);
-		if (!ret)
-			continue;
-
-		/* running? */
-		ret = pk_obj_list_exists (PK_OBJ_LIST(post->priv->running_exec_list), files[i]);
-		if (!ret)
-			continue;
-
-		/* TODO: findout if the executable has a desktop file, and if so,
-		 * suggest an application restart instead */
-
-		/* send signal about session restart */
-		egg_debug ("package %s updated, and %s is running", id->name, files[i]);
-		pk_backend_require_restart (post->priv->backend, PK_RESTART_ENUM_SESSION, package_id);
-	}
-	g_strfreev (files);
-	pk_package_id_free (id);
-}
-
-#ifdef USE_SECURITY_POLKIT
-/**
- * dkp_post_trans_get_cmdline:
- **/
-static gchar *
-dkp_post_trans_get_cmdline (guint pid)
-{
-	gboolean ret;
-	gchar *filename = NULL;
-	gchar *cmdline = NULL;
-	GError *error = NULL;
-
-	/* get command line from proc */
-	filename = g_strdup_printf ("/proc/%i/cmdline", pid);
-	ret = g_file_get_contents (filename, &cmdline, NULL, &error);
-	if (!ret) {
-		egg_debug ("failed to get cmdline: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-out:
-	g_free (filename);
-	return cmdline;
-}
-#endif
-
-/**
- * pk_post_trans_update_process_list:
- **/
-static gboolean
-pk_post_trans_update_process_list (PkPostTrans *post)
-{
-	GDir *dir;
-	const gchar *name;
-	gchar *offset;
-	gchar *uid_file;
-	gchar *contents;
-	gboolean ret;
-	guint uid;
-	pid_t pid;
-	gchar *exec;
-
-	uid = getuid ();
-	dir = g_dir_open ("/proc", 0, NULL);
-	name = g_dir_read_name (dir);
-	pk_obj_list_clear (PK_OBJ_LIST(post->priv->running_exec_list));
-	while (name != NULL) {
-		contents = NULL;
-		uid_file = g_build_filename ("/proc", name, "loginuid", NULL);
-
-		/* is a process file */
-		if (!g_file_test (uid_file, G_FILE_TEST_EXISTS))
-			goto out;
-
-		/* able to get contents */
-		ret = g_file_get_contents (uid_file, &contents, 0, NULL);
-		if (!ret)
-			goto out;
-
-		/* is run by our UID */
-		uid = atoi (contents);
-
-		/* get the exec for the pid */
-		pid = atoi (name);
-#ifdef USE_SECURITY_POLKIT
-		exec = dkp_post_trans_get_cmdline (pid);
-		if (exec == NULL)
-			goto out;
-#else
-		goto out;
-#endif
-
-		/* can be /usr/libexec/notification-daemon.#prelink#.9sOhao */
-		offset = g_strrstr (exec, ".#prelink#.");
-		if (offset != NULL)
-			*(offset) = '\0';
-		egg_debug ("uid=%i, pid=%i, exec=%s", uid, pid, exec);
-		pk_obj_list_add (PK_OBJ_LIST(post->priv->running_exec_list), exec);
-out:
-		g_free (uid_file);
-		g_free (contents);
-		name = g_dir_read_name (dir);
-	}
-	g_dir_close (dir);
-	return TRUE;
-}
-
-/**
- * pk_post_trans_check_running_process:
- **/
-gboolean
-pk_post_trans_check_running_process (PkPostTrans *post, gchar **package_ids)
-{
-	PkStore *store;
-	guint signal_files;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-
-	if (post->priv->backend->desc->get_files == NULL) {
-		egg_debug ("cannot get files");
-		return FALSE;
-	}
-
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
-	pk_post_trans_set_progress_changed (post, 101);
-
-	store = pk_backend_get_store (post->priv->backend);
-	pk_post_trans_update_process_list (post);
-
-	signal_files = g_signal_connect (post->priv->backend, "files",
-					 G_CALLBACK (pk_post_trans_update_files_check_running_cb), post);
-
-	/* get all the files touched in the packages we just updated */
-	pk_backend_reset (post->priv->backend);
-	pk_store_set_strv (store, "package_ids", package_ids);
-	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
-
-	/* wait for finished */
-	g_main_loop_run (post->priv->loop);
-
-	g_signal_handler_disconnect (post->priv->backend, signal_files);
-	pk_post_trans_set_progress_changed (post, 100);
-	return TRUE;
-}
-
-/**
- * pk_post_trans_update_files_check_desktop_cb:
- **/
-static void
-pk_post_trans_update_files_check_desktop_cb (PkBackend *backend, const gchar *package_id,
-					     const gchar *filelist, PkPostTrans *post)
-{
-	guint i;
-	guint len;
-	gboolean ret;
-	gchar **files;
-	gchar **package;
-	PkPackageId *id;
-	gchar *md5;
-
-	id = pk_package_id_new_from_string (package_id);
-	files = g_strsplit (filelist, ";", 0);
-	package = g_strsplit (package_id, ";", 0);
-
-	/* check each file */
-	len = g_strv_length (files);
-	for (i=0; i<len; i++) {
-		/* exists? */
-		ret = g_file_test (files[i], G_FILE_TEST_EXISTS);
-		if (!ret)
-			continue;
-
-		/* .desktop file? */
-		ret = g_str_has_suffix (files[i], ".desktop");
-		if (!ret)
-			continue;
-
-		egg_debug ("adding filename %s", files[i]);
-		md5 = pk_post_trans_get_filename_md5 (files[i]);
-		pk_post_trans_sqlite_add_filename_details (post, files[i], package[0], md5);
-		g_free (md5);
-	}
-	g_strfreev (files);
-	g_strfreev (package);
-	pk_package_id_free (id);
-}
-
-/**
- * pk_post_trans_check_desktop_files:
- **/
-gboolean
-pk_post_trans_check_desktop_files (PkPostTrans *post, gchar **package_ids)
-{
-	PkStore *store;
-	guint signal_files;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-
-	if (post->priv->backend->desc->get_files == NULL) {
-		egg_debug ("cannot get files");
-		return FALSE;
-	}
-
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
-	pk_post_trans_set_progress_changed (post, 101);
-
-	store = pk_backend_get_store (post->priv->backend);
-	signal_files = g_signal_connect (post->priv->backend, "files",
-					 G_CALLBACK (pk_post_trans_update_files_check_desktop_cb), post);
-
-	/* get all the files touched in the packages we just updated */
-	pk_backend_reset (post->priv->backend);
-	pk_store_set_strv (store, "package_ids", package_ids);
-	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
-
-	/* wait for finished */
-	g_main_loop_run (post->priv->loop);
-
-	g_signal_handler_disconnect (post->priv->backend, signal_files);
-	pk_post_trans_set_progress_changed (post, 100);
-	return TRUE;
-}
-
-/**
- * pk_post_trans_files_check_library_restart_cb:
- **/
-static void
-pk_post_trans_files_check_library_restart_cb (PkBackend *backend, const gchar *package_id,
-					      const gchar *filelist, PkPostTrans *post)
-{
-	guint i;
-	guint len;
-	gchar **files = NULL;
-
-	files = g_strsplit (filelist, ";", 0);
-
-	/* check each file to see if it's a system shared library */
-	len = g_strv_length (files);
-	for (i=0; i<len; i++) {
-		/* not a system library */
-		if (strstr (files[i], "/lib/") == NULL)
-			continue;
-
-		/* not a shared object */
-		if (strstr (files[i], ".so") == NULL)
-			continue;
-
-		/* add as it matches the criteria */
-		egg_debug ("adding filename %s", files[i]);
-		g_ptr_array_add (post->priv->files_list, g_strdup (files[i]));
-	}
-}
-
-/**
- * pk_post_trans_get_cmdline:
- **/
-static gchar *
-pk_post_trans_get_cmdline (PkPostTrans *post, guint pid)
-{
-	gboolean ret;
-	gchar *filename = NULL;
-	gchar *cmdline = NULL;
-	GError *error = NULL;
-
-	/* get command line from proc */
-	filename = g_strdup_printf ("/proc/%i/cmdline", pid);
-	ret = g_file_get_contents (filename, &cmdline, NULL, &error);
-	if (!ret) {
-		egg_warning ("failed to get cmdline: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-out:
-	g_free (filename);
-	return cmdline;
-}
-
-/**
- * pk_post_trans_get_uid:
- **/
-static gint
-pk_post_trans_get_uid (PkPostTrans *post, guint pid)
-{
-	gboolean ret;
-	gint uid = -1;
-	gchar *filename = NULL;
-	gchar *uid_text = NULL;
-	GError *error = NULL;
-
-	/* get command line from proc */
-	filename = g_strdup_printf ("/proc/%i/loginuid", pid);
-	ret = g_file_get_contents (filename, &uid_text, NULL, &error);
-	if (!ret) {
-		egg_warning ("failed to get cmdline: %s", error->message);
-		g_error_free (error);
-		goto out;
-	}
-
-	/* convert from text */
-	uid = atoi (uid_text);
-out:
-	g_free (filename);
-	g_free (uid_text);
-	return uid;
-}
-
-/**
- * pk_post_trans_check_library_restart_emit:
- **/
-static gboolean
-pk_post_trans_check_library_restart_emit (PkPostTrans *post, GPtrArray *pids)
-{
-	gint uid;
-	guint i;
-	guint pid;
-	gchar *filename;
-	gchar *cmdline;
-	gchar *cmdline_full;
-	gchar *package_id;
-	GPtrArray *files_session;
-	GPtrArray *files_system;
-	const PkPackageObj *obj;
-
-	/* create arrays */
-	files_session = g_ptr_array_new ();
-	files_system = g_ptr_array_new ();
-
-	/* find the package name of each pid */
-	for (i=0; i<pids->len; i++) {
-		pid = GPOINTER_TO_INT (g_ptr_array_index (pids, i));
-
-		/* get user */
-		uid = pk_post_trans_get_uid (post, pid);
-		if (uid < 0)
-			continue;
-
-		/* get command line */
-		cmdline = pk_post_trans_get_cmdline (post, pid);
-		if (cmdline == NULL)
-			continue;
-
-		/* prepend path if it does not already exist */
-		if (cmdline[0] == '/')
-			cmdline_full = g_strdup (cmdline);
-		else
-			cmdline_full = g_strdup_printf ("/usr/bin/%s", cmdline);
-
-		egg_warning ("pid=%i: %s (%i)", pid, cmdline_full, uid);
-		if (uid < 500)
-			g_ptr_array_add (files_system, cmdline_full);
-		else
-			g_ptr_array_add (files_session, cmdline_full);
-		g_free (cmdline);
-	}
-
-	/* we found nothing */
-	if (files_system->len == 0 && files_session->len == 0) {
-		egg_warning ("no pids could be resolved");
-		goto out;
-	}
-
-	/* process all session restarts */
-	for (i=0; i<files_session->len; i++) {
-		filename = g_ptr_array_index (files_session, i);
-
-		obj = pk_post_trans_get_installed_package_for_file (post, filename);
-		if (obj == NULL) {
-			egg_warning ("failed to find package for %s", filename);
-			continue;
-		}
-
-		package_id = pk_package_id_to_string (obj->id);
-		pk_post_trans_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SESSION, package_id);
-		g_free (package_id);
-	}
-
-	/* process all system restarts */
-	for (i=0; i<files_system->len; i++) {
-		filename = g_ptr_array_index (files_system, i);
-
-		obj = pk_post_trans_get_installed_package_for_file (post, filename);
-		if (obj == NULL) {
-			egg_warning ("failed to find package for %s", filename);
-			continue;
-		}
-
-		package_id = pk_package_id_to_string (obj->id);
-		pk_post_trans_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SYSTEM, package_id);
-		g_free (package_id);
-	}
-
-out:
-	g_ptr_array_foreach (files_session, (GFunc) g_free, NULL);
-	g_ptr_array_foreach (files_system, (GFunc) g_free, NULL);
-	g_ptr_array_free (files_session, TRUE);
-	g_ptr_array_free (files_system, TRUE);
-	return TRUE;
-}
-
-/**
- * pk_post_trans_check_library_restart:
- **/
-gboolean
-pk_post_trans_check_library_restart (PkPostTrans *post, gchar **package_ids)
-{
-	PkStore *store;
-	guint signal_files;
-	gboolean ret = TRUE;
-	gchar **files = NULL;
-	GPtrArray *pids;
-
-	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
-
-	if (post->priv->backend->desc->get_files == NULL) {
-		egg_debug ("cannot get files");
-		return FALSE;
-	}
-
-	/* reset */
-	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
-	g_ptr_array_set_size (post->priv->files_list, 0);
-
-	/* get list from lsof */
-	ret = pk_lsof_refresh (post->priv->lsof);
-	if (!ret) {
-		egg_warning ("failed to refresh");
-		goto out;
-	}
-
-	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
-	pk_post_trans_set_progress_changed (post, 101);
-
-	store = pk_backend_get_store (post->priv->backend);
-	signal_files = g_signal_connect (post->priv->backend, "files",
-					 G_CALLBACK (pk_post_trans_files_check_library_restart_cb), post);
-
-	/* get all the files touched in the packages we just updated */
-	pk_backend_reset (post->priv->backend);
-	pk_store_set_strv (store, "package_ids", package_ids);
-	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
-
-	/* wait for finished */
-	g_main_loop_run (post->priv->loop);
-
-	/* nothing to do */
-	if (post->priv->files_list->len == 0) {
-		egg_warning ("no files");
-		goto out;
-	}
-
-	/* get the list of PIDs */
-	files = pk_ptr_array_to_strv (post->priv->files_list);
-	pids = pk_lsof_get_pids_for_filenames (post->priv->lsof, files);
-
-	/* nothing depends on these libraries */
-	if (pids == NULL) {
-		egg_warning ("failed to get process list");
-		goto out;
-	}
-
-	/* nothing depends on these libraries */
-	if (pids->len == 0) {
-		egg_warning ("no processes depend on these libraries");
-		goto out;
-	}
-
-	/* emit */
-	pk_post_trans_check_library_restart_emit (post, pids);
-	g_ptr_array_free (pids, TRUE);
-
-	g_signal_handler_disconnect (post->priv->backend, signal_files);
-	pk_post_trans_set_progress_changed (post, 100);
-out:
-	g_strfreev (files);
-	return ret;
-}
-
-/**
- * pk_post_trans_finalize:
- **/
-static void
-pk_post_trans_finalize (GObject *object)
-{
-	PkPostTrans *post;
-
-	g_return_if_fail (object != NULL);
-	g_return_if_fail (PK_IS_POST_TRANS (object));
-	post = PK_POST_TRANS (object);
-
-	g_signal_handler_disconnect (post->priv->backend, post->priv->finished_id);
-	g_signal_handler_disconnect (post->priv->backend, post->priv->package_id);
-
-	if (g_main_loop_is_running (post->priv->loop))
-		g_main_loop_quit (post->priv->loop);
-	g_main_loop_unref (post->priv->loop);
-	sqlite3_close (post->priv->db);
-	g_hash_table_unref (post->priv->hash);
-	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
-	g_ptr_array_free (post->priv->files_list, TRUE);
-
-	g_object_unref (post->priv->backend);
-	g_object_unref (post->priv->lsof);
-	g_object_unref (post->priv->list);
-	g_object_unref (post->priv->running_exec_list);
-
-	G_OBJECT_CLASS (pk_post_trans_parent_class)->finalize (object);
-}
-
-/**
- * pk_post_trans_class_init:
- **/
-static void
-pk_post_trans_class_init (PkPostTransClass *klass)
-{
-	GObjectClass *object_class = G_OBJECT_CLASS (klass);
-	object_class->finalize = pk_post_trans_finalize;
-	signals [PK_POST_TRANS_STATUS_CHANGED] =
-		g_signal_new ("status-changed",
-			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
-			      0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
-			      G_TYPE_NONE, 1, G_TYPE_UINT);
-	signals [PK_POST_TRANS_PROGRESS_CHANGED] =
-		g_signal_new ("progress-changed",
-			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
-			      0, NULL, NULL, pk_marshal_VOID__UINT_UINT_UINT_UINT,
-			      G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
-	signals [PK_POST_TRANS_REQUIRE_RESTART] =
-		g_signal_new ("require-restart",
-			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
-			      0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
-			      G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
-	g_type_class_add_private (klass, sizeof (PkPostTransPrivate));
-}
-
-/**
- * pk_post_trans_init:
- *
- * initializes the post_trans class. NOTE: We expect post_trans objects
- * to *NOT* be removed or added during the session.
- * We only control the first post_trans object if there are more than one.
- **/
-static void
-pk_post_trans_init (PkPostTrans *post)
-{
-	gboolean ret;
-	const gchar *statement;
-	gchar *error_msg = NULL;
-	gint rc;
-
-	post->priv = PK_POST_TRANS_GET_PRIVATE (post);
-	post->priv->running_exec_list = pk_post_trans_string_list_new ();
-	post->priv->loop = g_main_loop_new (NULL, FALSE);
-	post->priv->list = pk_package_list_new ();
-	post->priv->backend = pk_backend_new ();
-	post->priv->lsof = pk_lsof_new ();
-	post->priv->db = NULL;
-	post->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
-	post->priv->files_list = g_ptr_array_new ();
-
-	post->priv->finished_id =
-		g_signal_connect (post->priv->backend, "finished",
-				  G_CALLBACK (pk_post_trans_finished_cb), post);
-	post->priv->package_id =
-		g_signal_connect (post->priv->backend, "package",
-				  G_CALLBACK (pk_post_trans_package_cb), post);
-
-	/* check if exists */
-	ret = g_file_test (PK_DESKTOP_DEFAULT_DATABASE, G_FILE_TEST_EXISTS);
-
-	egg_debug ("trying to open database '%s'", PK_DESKTOP_DEFAULT_DATABASE);
-	rc = sqlite3_open (PK_DESKTOP_DEFAULT_DATABASE, &post->priv->db);
-	if (rc != 0) {
-		egg_warning ("Can't open database: %s\n", sqlite3_errmsg (post->priv->db));
-		sqlite3_close (post->priv->db);
-		post->priv->db = NULL;
-		return;
-	}
-
-	/* create if not exists */
-	if (!ret) {
-		egg_debug ("creating database cache in %s", PK_DESKTOP_DEFAULT_DATABASE);
-		statement = "CREATE TABLE cache ("
-			    "filename TEXT,"
-			    "package TEXT,"
-			    "show INTEGER,"
-			    "md5 TEXT);";
-		rc = sqlite3_exec (post->priv->db, statement, NULL, NULL, &error_msg);
-		if (rc != SQLITE_OK) {
-			egg_warning ("SQL error: %s\n", error_msg);
-			sqlite3_free (error_msg);
-		}
-	}
-
-	/* we don't need to keep syncing */
-	sqlite3_exec (post->priv->db, "PRAGMA synchronous=OFF", NULL, NULL, NULL);
-}
-
-/**
- * pk_post_trans_new:
- * Return value: A new post_trans class instance.
- **/
-PkPostTrans *
-pk_post_trans_new (void)
-{
-	PkPostTrans *post;
-	post = g_object_new (PK_TYPE_POST_TRANS, NULL);
-	return PK_POST_TRANS (post);
-}
-
-/***************************************************************************
- ***                          MAKE CHECK TESTS                           ***
- ***************************************************************************/
-#ifdef EGG_TEST
-#include "egg-test.h"
-
-void
-egg_test_post_trans (EggTest *test)
-{
-	PkPostTrans *post;
-
-	if (!egg_test_start (test, "PkPostTrans"))
-		return;
-
-	/************************************************************/
-	egg_test_title (test, "get an instance");
-	post = pk_post_trans_new ();
-	egg_test_assert (test, post != NULL);
-
-	g_object_unref (post);
-
-	egg_test_end (test);
-}
-#endif
-
diff --git a/src/pk-post-trans.h b/src/pk-post-trans.h
deleted file mode 100644
index 7f2fe9f..0000000
--- a/src/pk-post-trans.h
+++ /dev/null
@@ -1,65 +0,0 @@
-/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
- *
- * Copyright (C) 2008 Richard Hughes <richard at hughsie.com>
- *
- * Licensed under the GNU General Public License Version 2
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
- */
-
-#ifndef __PK_POST_TRANS_H
-#define __PK_POST_TRANS_H
-
-#include <glib-object.h>
-
-G_BEGIN_DECLS
-
-#define PK_TYPE_POST_TRANS		(pk_post_trans_get_type ())
-#define PK_POST_TRANS(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), PK_TYPE_POST_TRANS, PkPostTrans))
-#define PK_POST_TRANS_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), PK_TYPE_POST_TRANS, PkPostTransClass))
-#define PK_IS_POST_TRANS(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), PK_TYPE_POST_TRANS))
-#define PK_IS_POST_TRANS_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), PK_TYPE_POST_TRANS))
-#define PK_POST_TRANS_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), PK_TYPE_POST_TRANS, PkPostTransClass))
-
-typedef struct PkPostTransPrivate PkPostTransPrivate;
-
-typedef struct
-{
-	GObject		      parent;
-	PkPostTransPrivate     *priv;
-} PkPostTrans;
-
-typedef struct
-{
-	GObjectClass	parent_class;
-} PkPostTransClass;
-
-GType		 pk_post_trans_get_type			(void);
-PkPostTrans	*pk_post_trans_new			(void);
-
-gboolean	 pk_post_trans_clear_firmware_requests	(PkPostTrans	*post);
-gboolean	 pk_post_trans_update_package_list	(PkPostTrans	*post);
-gboolean	 pk_post_trans_import_desktop_files	(PkPostTrans	*post);
-gboolean	 pk_post_trans_check_running_process	(PkPostTrans	*post,
-							 gchar		**package_ids);
-gboolean	 pk_post_trans_check_desktop_files	(PkPostTrans	*post,
-							 gchar		**package_ids);
-gboolean	 pk_post_trans_check_library_restart	(PkPostTrans	*post,
-							 gchar		**package_ids);
-
-G_END_DECLS
-
-#endif /* __PK_POST_TRANS_H */
-
diff --git a/src/pk-transaction-extra.c b/src/pk-transaction-extra.c
new file mode 100644
index 0000000..740ba8a
--- /dev/null
+++ b/src/pk-transaction-extra.c
@@ -0,0 +1,1163 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008-2009 Richard Hughes <richard at hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <packagekit-glib/packagekit.h>
+#include <gio/gdesktopappinfo.h>
+#include <sqlite3.h>
+
+#include "egg-debug.h"
+
+#include "pk-transaction-extra.h"
+#include "pk-shared.h"
+#include "pk-marshal.h"
+#include "pk-backend-internal.h"
+#include "pk-lsof.h"
+
+#define PK_POST_TRANS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_POST_TRANS, PkTransactionExtraPrivate))
+
+struct PkTransactionExtraPrivate
+{
+	sqlite3			*db;
+	PkBackend		*backend;
+	GMainLoop		*loop;
+	PkObjList		*running_exec_list;
+	PkPackageList		*list;
+	PkLsof			*lsof;
+	guint			 finished_id;
+	guint			 package_id;
+	GHashTable		*hash;
+	GPtrArray		*files_list;
+};
+
+enum {
+	PK_POST_TRANS_STATUS_CHANGED,
+	PK_POST_TRANS_PROGRESS_CHANGED,
+	PK_POST_TRANS_REQUIRE_RESTART,
+	PK_POST_TRANS_LAST_SIGNAL
+};
+
+static guint signals [PK_POST_TRANS_LAST_SIGNAL] = { 0 };
+G_DEFINE_TYPE (PkTransactionExtra, pk_transaction_extra, G_TYPE_OBJECT)
+
+/**
+ * pk_transaction_extra_finished_cb:
+ **/
+static void
+pk_transaction_extra_finished_cb (PkBackend *backend, PkExitEnum exit_enum, PkTransactionExtra *post)
+{
+	if (g_main_loop_is_running (post->priv->loop))
+		g_main_loop_quit (post->priv->loop);
+}
+
+/**
+ * pk_transaction_extra_package_cb:
+ **/
+static void
+pk_transaction_extra_package_cb (PkBackend *backend, const PkPackageObj *obj, PkTransactionExtra *post)
+{
+	pk_obj_list_add (PK_OBJ_LIST(post->priv->list), obj);
+}
+
+/**
+ * pk_transaction_extra_set_require_restart:
+ **/
+static void
+pk_transaction_extra_set_require_restart (PkTransactionExtra *post, PkRestartEnum restart, const gchar *package_id)
+{
+	egg_debug ("emit require-restart %s, %s", pk_restart_enum_to_text (restart), package_id);
+	g_signal_emit (post, signals [PK_POST_TRANS_REQUIRE_RESTART], 0, restart, package_id);
+}
+
+/**
+ * pk_transaction_extra_set_status_changed:
+ **/
+static void
+pk_transaction_extra_set_status_changed (PkTransactionExtra *post, PkStatusEnum status)
+{
+	egg_debug ("emiting status-changed %s", pk_status_enum_to_text (status));
+	g_signal_emit (post, signals [PK_POST_TRANS_STATUS_CHANGED], 0, status);
+}
+
+/**
+ * pk_transaction_extra_set_progress_changed:
+ **/
+static void
+pk_transaction_extra_set_progress_changed (PkTransactionExtra *post, guint percentage)
+{
+	egg_debug ("emiting progress-changed %i", percentage);
+	g_signal_emit (post, signals [PK_POST_TRANS_PROGRESS_CHANGED], 0, percentage, 0, 0, 0);
+}
+
+/**
+ * pk_transaction_extra_get_installed_package_for_file:
+ **/
+static const PkPackageObj *
+pk_transaction_extra_get_installed_package_for_file (PkTransactionExtra *post, const gchar *filename)
+{
+	guint size;
+	const PkPackageObj *obj = NULL;
+	PkStore *store;
+
+	/* use PK to find the correct package */
+	pk_obj_list_clear (PK_OBJ_LIST(post->priv->list));
+	pk_backend_reset (post->priv->backend);
+	store = pk_backend_get_store (post->priv->backend);
+	pk_store_set_uint (store, "filters", pk_bitfield_value (PK_FILTER_ENUM_INSTALLED));
+	pk_store_set_string (store, "search", filename);
+	post->priv->backend->desc->search_file (post->priv->backend, pk_bitfield_value (PK_FILTER_ENUM_INSTALLED), filename);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	/* check that we only matched one package */
+	size = pk_package_list_get_size (post->priv->list);
+	if (size != 1) {
+		egg_warning ("not correct size, %i", size);
+		goto out;
+	}
+
+	/* get the obj */
+	obj = pk_package_list_get_obj (post->priv->list, 0);
+	if (obj == NULL) {
+		egg_warning ("cannot get obj");
+		goto out;
+	}
+out:
+	return obj;
+}
+
+/**
+ * pk_transaction_extra_string_list_new:
+ **/
+static PkObjList *
+pk_transaction_extra_string_list_new ()
+{
+	PkObjList *list;
+	list = pk_obj_list_new ();
+	pk_obj_list_set_compare (list, (PkObjListCompareFunc) g_strcmp0);
+	pk_obj_list_set_copy (list, (PkObjListCopyFunc) g_strdup);
+	pk_obj_list_set_free (list, (PkObjListFreeFunc) g_free);
+	pk_obj_list_set_to_string (list, (PkObjListToStringFunc) g_strdup);
+	pk_obj_list_set_from_string (list, (PkObjListFromStringFunc) g_strdup);
+	return list;
+}
+
+/**
+ * pk_transaction_extra_get_filename_md5:
+ **/
+static gchar *
+pk_transaction_extra_get_filename_md5 (const gchar *filename)
+{
+	gchar *md5 = NULL;
+	gchar *data = NULL;
+	gsize length;
+	GError *error = NULL;
+	gboolean ret;
+
+	/* check is no longer exists */
+	ret = g_file_test (filename, G_FILE_TEST_EXISTS);
+	if (!ret)
+		goto out;
+
+	/* get data */
+	ret = g_file_get_contents (filename, &data, &length, &error);
+	if (!ret) {
+		egg_warning ("failed to open file %s: %s", filename, error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* check md5 is same */
+	md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5, (const guchar *) data, length);
+out:
+	g_free (data);
+	return md5;
+}
+
+/**
+ * pk_transaction_extra_sqlite_remove_filename:
+ **/
+static gint
+pk_transaction_extra_sqlite_remove_filename (PkTransactionExtra *post, const gchar *filename)
+{
+	gchar *statement;
+	gint rc;
+
+	statement = g_strdup_printf ("DELETE FROM cache WHERE filename = '%s'", filename);
+	rc = sqlite3_exec (post->priv->db, statement, NULL, NULL, NULL);
+	g_free (statement);
+	return rc;
+}
+
+/**
+ * pk_transaction_extra_sqlite_add_filename_details:
+ **/
+static gint
+pk_transaction_extra_sqlite_add_filename_details (PkTransactionExtra *post, const gchar *filename, const gchar *package, const gchar *md5)
+{
+	gchar *statement;
+	gchar *error_msg = NULL;
+	sqlite3_stmt *sql_statement = NULL;
+	gint rc = -1;
+	gint show;
+	GDesktopAppInfo *info;
+
+	/* find out if we should show desktop file in menus */
+	info = g_desktop_app_info_new_from_filename (filename);
+	if (info == NULL) {
+		egg_warning ("could not load desktop file %s", filename);
+		goto out;
+	}
+	show = g_app_info_should_show (G_APP_INFO (info));
+	g_object_unref (info);
+
+	egg_debug ("add filename %s from %s with md5: %s (show: %i)", filename, package, md5, show);
+
+	/* the row might already exist */
+	statement = g_strdup_printf ("DELETE FROM cache WHERE filename = '%s'", filename);
+	sqlite3_exec (post->priv->db, statement, NULL, NULL, NULL);
+	g_free (statement);
+
+	/* prepare the query, as we don't escape it */
+	rc = sqlite3_prepare_v2 (post->priv->db, "INSERT INTO cache (filename, package, show, md5) VALUES (?, ?, ?, ?)", -1, &sql_statement, NULL);
+	if (rc != SQLITE_OK) {
+		egg_warning ("SQL failed to prepare: %s", sqlite3_errmsg (post->priv->db));
+		goto out;
+	}
+
+	/* add data */
+	sqlite3_bind_text (sql_statement, 1, filename, -1, SQLITE_STATIC);
+	sqlite3_bind_text (sql_statement, 2, package, -1, SQLITE_STATIC);
+	sqlite3_bind_int (sql_statement, 3, show);
+	sqlite3_bind_text (sql_statement, 4, md5, -1, SQLITE_STATIC);
+
+	/* save this */
+	sqlite3_step (sql_statement);
+	rc = sqlite3_finalize (sql_statement);
+	if (rc != SQLITE_OK) {
+		egg_warning ("SQL error: %s\n", error_msg);
+		sqlite3_free (error_msg);
+		goto out;
+	}
+
+out:
+	return rc;
+}
+
+/**
+ * pk_transaction_extra_sqlite_add_filename:
+ **/
+static gint
+pk_transaction_extra_sqlite_add_filename (PkTransactionExtra *post, const gchar *filename, const gchar *md5_opt)
+{
+	gchar *md5 = NULL;
+	gchar *package = NULL;
+	gint rc = -1;
+	const PkPackageObj *obj;
+
+	/* if we've got it, use old data */
+	if (md5_opt != NULL)
+		md5 = g_strdup (md5_opt);
+	else
+		md5 = pk_transaction_extra_get_filename_md5 (filename);
+
+	/* resolve */
+	obj = pk_transaction_extra_get_installed_package_for_file (post, filename);
+	if (obj == NULL) {
+		egg_warning ("failed to get list");
+		goto out;
+	}
+
+	/* add */
+	rc = pk_transaction_extra_sqlite_add_filename_details (post, filename, obj->id->name, md5);
+out:
+	g_free (md5);
+	g_free (package);
+	return rc;
+}
+
+/**
+ * pk_transaction_extra_sqlite_cache_rescan_cb:
+ **/
+static gint
+pk_transaction_extra_sqlite_cache_rescan_cb (void *data, gint argc, gchar **argv, gchar **col_name)
+{
+	PkTransactionExtra *post = PK_POST_TRANS (data);
+	const gchar *filename = NULL;
+	const gchar *md5 = NULL;
+	gchar *md5_calc = NULL;
+	gint i;
+
+	/* add the filename data to the array */
+	for (i=0; i<argc; i++) {
+		if (g_strcmp0 (col_name[i], "filename") == 0 && argv[i] != NULL)
+			filename = argv[i];
+		else if (g_strcmp0 (col_name[i], "md5") == 0 && argv[i] != NULL)
+			md5 = argv[i];
+	}
+
+	/* sanity check */
+	if (filename == NULL || md5 == NULL) {
+		egg_warning ("filename %s and md5 %s)", filename, md5);
+		goto out;
+	}
+
+	/* get md5 */
+	md5_calc = pk_transaction_extra_get_filename_md5 (filename);
+	if (md5_calc == NULL) {
+		egg_debug ("remove of %s as no longer found", filename);
+		pk_transaction_extra_sqlite_remove_filename (post, filename);
+		goto out;
+	}
+
+	/* we've checked the file */
+	g_hash_table_insert (post->priv->hash, g_strdup (filename), GUINT_TO_POINTER (1));
+
+	/* check md5 is same */
+	if (g_strcmp0 (md5, md5_calc) != 0) {
+		egg_debug ("add of %s as md5 invalid (%s vs %s)", filename, md5, md5_calc);
+		pk_transaction_extra_sqlite_add_filename (post, filename, md5_calc);
+	}
+
+	egg_debug ("existing filename %s valid, md5=%s", filename, md5);
+out:
+	g_free (md5_calc);
+	return 0;
+}
+
+/**
+ * pk_transaction_extra_import_desktop_files:
+ **/
+gboolean
+pk_transaction_extra_import_desktop_files (PkTransactionExtra *post)
+{
+	gchar *statement;
+	gchar *error_msg = NULL;
+	gint rc;
+	GError *error = NULL;
+	GDir *dir;
+	const gchar *filename;
+	gpointer data;
+	gchar *path;
+	GPtrArray *array;
+	gfloat step;
+	guint i;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+	g_return_val_if_fail (post->priv->db != NULL, FALSE);
+
+	if (post->priv->backend->desc->search_file == NULL) {
+		egg_debug ("cannot search files");
+		return FALSE;
+	}
+
+	/* use a local backend instance */
+	pk_backend_reset (post->priv->backend);
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+
+	/* reset hash */
+	g_hash_table_remove_all (post->priv->hash);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
+	/* first go through the existing data, and look for modifications and removals */
+	statement = g_strdup ("SELECT filename, md5 FROM cache");
+	rc = sqlite3_exec (post->priv->db, statement, pk_transaction_extra_sqlite_cache_rescan_cb, post, &error_msg);
+	g_free (statement);
+	if (rc != SQLITE_OK) {
+		egg_warning ("SQL error: %s\n", error_msg);
+		sqlite3_free (error_msg);
+	}
+
+	/* open directory */
+	dir = g_dir_open (PK_DESKTOP_DEFAULT_APPLICATION_DIR, 0, &error);
+	if (dir == NULL) {
+		egg_warning ("failed to open file %s: %s", PK_DESKTOP_DEFAULT_APPLICATION_DIR, error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* go through desktop files and add them to an array if not present */
+	filename = g_dir_read_name (dir);
+	array = g_ptr_array_new ();
+	while (filename != NULL) {
+		if (g_str_has_suffix (filename, ".desktop")) {
+			path = g_build_filename (PK_DESKTOP_DEFAULT_APPLICATION_DIR, filename, NULL);
+			data = g_hash_table_lookup (post->priv->hash, path);
+			if (data == NULL) {
+				egg_debug ("add of %s as not present in db", path);
+				g_ptr_array_add (array, g_strdup (path));
+			}
+			g_free (path);
+		}
+		filename = g_dir_read_name (dir);
+	}
+	g_dir_close (dir);
+
+	step = 100.0f / array->len;
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST);
+
+	/* process files in an array */
+	for (i=0; i<array->len; i++) {
+		pk_transaction_extra_set_progress_changed (post, i * step);
+		path = g_ptr_array_index (array, i);
+		pk_transaction_extra_sqlite_add_filename (post, path, NULL);
+	}
+	g_ptr_array_foreach (array, (GFunc) g_free, NULL);
+	g_ptr_array_free (array, TRUE);
+
+out:
+	pk_transaction_extra_set_progress_changed (post, 100);
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_FINISHED);
+	return TRUE;
+}
+
+/**
+ * pk_transaction_extra_update_package_list:
+ **/
+gboolean
+pk_transaction_extra_update_package_list (PkTransactionExtra *post)
+{
+	gboolean ret;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	if (post->priv->backend->desc->get_packages == NULL) {
+		egg_debug ("cannot get packages");
+		return FALSE;
+	}
+
+	egg_debug ("updating package lists");
+
+	/* clear old list */
+	pk_obj_list_clear (PK_OBJ_LIST(post->priv->list));
+
+	/* update UI */
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_GENERATE_PACKAGE_LIST);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
+	/* get the new package list */
+	pk_backend_reset (post->priv->backend);
+	pk_store_set_uint (pk_backend_get_store (post->priv->backend), "filters", pk_bitfield_value (PK_FILTER_ENUM_NONE));
+	post->priv->backend->desc->get_packages (post->priv->backend, PK_FILTER_ENUM_NONE);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	/* update UI */
+	pk_transaction_extra_set_progress_changed (post, 90);
+
+	/* convert to a file */
+	ret = pk_obj_list_to_file (PK_OBJ_LIST(post->priv->list), PK_SYSTEM_PACKAGE_LIST_FILENAME);
+	if (!ret)
+		egg_warning ("failed to save to file");
+
+	/* update UI */
+	pk_transaction_extra_set_progress_changed (post, 100);
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_FINISHED);
+
+	return ret;
+}
+
+/**
+ * pk_transaction_extra_clear_firmware_requests:
+ **/
+gboolean
+pk_transaction_extra_clear_firmware_requests (PkTransactionExtra *post)
+{
+	gboolean ret;
+	gchar *filename;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	/* clear the firmware requests directory */
+	filename = g_build_filename (LOCALSTATEDIR, "run", "PackageKit", "udev", NULL);
+	egg_debug ("clearing udev firmware requests at %s", filename);
+	ret = pk_directory_remove_contents (filename);
+	if (!ret)
+		egg_warning ("failed to clear %s", filename);
+	g_free (filename);
+	return ret;
+}
+
+
+/**
+ * pk_transaction_extra_update_files_check_running_cb:
+ **/
+static void
+pk_transaction_extra_update_files_check_running_cb (PkBackend *backend, const gchar *package_id,
+					     const gchar *filelist, PkTransactionExtra *post)
+{
+	guint i;
+	guint len;
+	gboolean ret;
+	gchar **files;
+	PkPackageId *id;
+
+	id = pk_package_id_new_from_string (package_id);
+	files = g_strsplit (filelist, ";", 0);
+
+	/* check each file */
+	len = g_strv_length (files);
+	for (i=0; i<len; i++) {
+		/* executable? */
+		ret = g_file_test (files[i], G_FILE_TEST_IS_REGULAR | G_FILE_TEST_IS_EXECUTABLE | G_FILE_TEST_EXISTS);
+		if (!ret)
+			continue;
+
+		/* running? */
+		ret = pk_obj_list_exists (PK_OBJ_LIST(post->priv->running_exec_list), files[i]);
+		if (!ret)
+			continue;
+
+		/* TODO: findout if the executable has a desktop file, and if so,
+		 * suggest an application restart instead */
+
+		/* send signal about session restart */
+		egg_debug ("package %s updated, and %s is running", id->name, files[i]);
+		pk_backend_require_restart (post->priv->backend, PK_RESTART_ENUM_SESSION, package_id);
+	}
+	g_strfreev (files);
+	pk_package_id_free (id);
+}
+
+#ifdef USE_SECURITY_POLKIT
+/**
+ * dkp_post_trans_get_cmdline:
+ **/
+static gchar *
+dkp_post_trans_get_cmdline (guint pid)
+{
+	gboolean ret;
+	gchar *filename = NULL;
+	gchar *cmdline = NULL;
+	GError *error = NULL;
+
+	/* get command line from proc */
+	filename = g_strdup_printf ("/proc/%i/cmdline", pid);
+	ret = g_file_get_contents (filename, &cmdline, NULL, &error);
+	if (!ret) {
+		egg_debug ("failed to get cmdline: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+out:
+	g_free (filename);
+	return cmdline;
+}
+#endif
+
+/**
+ * pk_transaction_extra_update_process_list:
+ **/
+static gboolean
+pk_transaction_extra_update_process_list (PkTransactionExtra *post)
+{
+	GDir *dir;
+	const gchar *name;
+	gchar *offset;
+	gchar *uid_file;
+	gchar *contents;
+	gboolean ret;
+	guint uid;
+	pid_t pid;
+	gchar *exec;
+
+	uid = getuid ();
+	dir = g_dir_open ("/proc", 0, NULL);
+	name = g_dir_read_name (dir);
+	pk_obj_list_clear (PK_OBJ_LIST(post->priv->running_exec_list));
+	while (name != NULL) {
+		contents = NULL;
+		uid_file = g_build_filename ("/proc", name, "loginuid", NULL);
+
+		/* is a process file */
+		if (!g_file_test (uid_file, G_FILE_TEST_EXISTS))
+			goto out;
+
+		/* able to get contents */
+		ret = g_file_get_contents (uid_file, &contents, 0, NULL);
+		if (!ret)
+			goto out;
+
+		/* is run by our UID */
+		uid = atoi (contents);
+
+		/* get the exec for the pid */
+		pid = atoi (name);
+#ifdef USE_SECURITY_POLKIT
+		exec = dkp_post_trans_get_cmdline (pid);
+		if (exec == NULL)
+			goto out;
+#else
+		goto out;
+#endif
+
+		/* can be /usr/libexec/notification-daemon.#prelink#.9sOhao */
+		offset = g_strrstr (exec, ".#prelink#.");
+		if (offset != NULL)
+			*(offset) = '\0';
+		egg_debug ("uid=%i, pid=%i, exec=%s", uid, pid, exec);
+		pk_obj_list_add (PK_OBJ_LIST(post->priv->running_exec_list), exec);
+out:
+		g_free (uid_file);
+		g_free (contents);
+		name = g_dir_read_name (dir);
+	}
+	g_dir_close (dir);
+	return TRUE;
+}
+
+/**
+ * pk_transaction_extra_check_running_process:
+ **/
+gboolean
+pk_transaction_extra_check_running_process (PkTransactionExtra *post, gchar **package_ids)
+{
+	PkStore *store;
+	guint signal_files;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	if (post->priv->backend->desc->get_files == NULL) {
+		egg_debug ("cannot get files");
+		return FALSE;
+	}
+
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
+	store = pk_backend_get_store (post->priv->backend);
+	pk_transaction_extra_update_process_list (post);
+
+	signal_files = g_signal_connect (post->priv->backend, "files",
+					 G_CALLBACK (pk_transaction_extra_update_files_check_running_cb), post);
+
+	/* get all the files touched in the packages we just updated */
+	pk_backend_reset (post->priv->backend);
+	pk_store_set_strv (store, "package_ids", package_ids);
+	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	g_signal_handler_disconnect (post->priv->backend, signal_files);
+	pk_transaction_extra_set_progress_changed (post, 100);
+	return TRUE;
+}
+
+/**
+ * pk_transaction_extra_update_files_check_desktop_cb:
+ **/
+static void
+pk_transaction_extra_update_files_check_desktop_cb (PkBackend *backend, const gchar *package_id,
+					     const gchar *filelist, PkTransactionExtra *post)
+{
+	guint i;
+	guint len;
+	gboolean ret;
+	gchar **files;
+	gchar **package;
+	PkPackageId *id;
+	gchar *md5;
+
+	id = pk_package_id_new_from_string (package_id);
+	files = g_strsplit (filelist, ";", 0);
+	package = g_strsplit (package_id, ";", 0);
+
+	/* check each file */
+	len = g_strv_length (files);
+	for (i=0; i<len; i++) {
+		/* exists? */
+		ret = g_file_test (files[i], G_FILE_TEST_EXISTS);
+		if (!ret)
+			continue;
+
+		/* .desktop file? */
+		ret = g_str_has_suffix (files[i], ".desktop");
+		if (!ret)
+			continue;
+
+		egg_debug ("adding filename %s", files[i]);
+		md5 = pk_transaction_extra_get_filename_md5 (files[i]);
+		pk_transaction_extra_sqlite_add_filename_details (post, files[i], package[0], md5);
+		g_free (md5);
+	}
+	g_strfreev (files);
+	g_strfreev (package);
+	pk_package_id_free (id);
+}
+
+/**
+ * pk_transaction_extra_check_desktop_files:
+ **/
+gboolean
+pk_transaction_extra_check_desktop_files (PkTransactionExtra *post, gchar **package_ids)
+{
+	PkStore *store;
+	guint signal_files;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	if (post->priv->backend->desc->get_files == NULL) {
+		egg_debug ("cannot get files");
+		return FALSE;
+	}
+
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
+	store = pk_backend_get_store (post->priv->backend);
+	signal_files = g_signal_connect (post->priv->backend, "files",
+					 G_CALLBACK (pk_transaction_extra_update_files_check_desktop_cb), post);
+
+	/* get all the files touched in the packages we just updated */
+	pk_backend_reset (post->priv->backend);
+	pk_store_set_strv (store, "package_ids", package_ids);
+	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	g_signal_handler_disconnect (post->priv->backend, signal_files);
+	pk_transaction_extra_set_progress_changed (post, 100);
+	return TRUE;
+}
+
+/**
+ * pk_transaction_extra_files_check_library_restart_cb:
+ **/
+static void
+pk_transaction_extra_files_check_library_restart_cb (PkBackend *backend, const gchar *package_id,
+					      const gchar *filelist, PkTransactionExtra *post)
+{
+	guint i;
+	guint len;
+	gchar **files = NULL;
+
+	files = g_strsplit (filelist, ";", 0);
+
+	/* check each file to see if it's a system shared library */
+	len = g_strv_length (files);
+	for (i=0; i<len; i++) {
+		/* not a system library */
+		if (strstr (files[i], "/lib/") == NULL)
+			continue;
+
+		/* not a shared object */
+		if (strstr (files[i], ".so") == NULL)
+			continue;
+
+		/* add as it matches the criteria */
+		egg_debug ("adding filename %s", files[i]);
+		g_ptr_array_add (post->priv->files_list, g_strdup (files[i]));
+	}
+}
+
+/**
+ * pk_transaction_extra_get_cmdline:
+ **/
+static gchar *
+pk_transaction_extra_get_cmdline (PkTransactionExtra *post, guint pid)
+{
+	gboolean ret;
+	gchar *filename = NULL;
+	gchar *cmdline = NULL;
+	GError *error = NULL;
+
+	/* get command line from proc */
+	filename = g_strdup_printf ("/proc/%i/cmdline", pid);
+	ret = g_file_get_contents (filename, &cmdline, NULL, &error);
+	if (!ret) {
+		egg_warning ("failed to get cmdline: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+out:
+	g_free (filename);
+	return cmdline;
+}
+
+/**
+ * pk_transaction_extra_get_uid:
+ **/
+static gint
+pk_transaction_extra_get_uid (PkTransactionExtra *post, guint pid)
+{
+	gboolean ret;
+	gint uid = -1;
+	gchar *filename = NULL;
+	gchar *uid_text = NULL;
+	GError *error = NULL;
+
+	/* get command line from proc */
+	filename = g_strdup_printf ("/proc/%i/loginuid", pid);
+	ret = g_file_get_contents (filename, &uid_text, NULL, &error);
+	if (!ret) {
+		egg_warning ("failed to get cmdline: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* convert from text */
+	uid = atoi (uid_text);
+out:
+	g_free (filename);
+	g_free (uid_text);
+	return uid;
+}
+
+/**
+ * pk_transaction_extra_check_library_restart_emit:
+ **/
+static gboolean
+pk_transaction_extra_check_library_restart_emit (PkTransactionExtra *post, GPtrArray *pids)
+{
+	gint uid;
+	guint i;
+	guint pid;
+	gchar *filename;
+	gchar *cmdline;
+	gchar *cmdline_full;
+	gchar *package_id;
+	GPtrArray *files_session;
+	GPtrArray *files_system;
+	const PkPackageObj *obj;
+
+	/* create arrays */
+	files_session = g_ptr_array_new ();
+	files_system = g_ptr_array_new ();
+
+	/* find the package name of each pid */
+	for (i=0; i<pids->len; i++) {
+		pid = GPOINTER_TO_INT (g_ptr_array_index (pids, i));
+
+		/* get user */
+		uid = pk_transaction_extra_get_uid (post, pid);
+		if (uid < 0)
+			continue;
+
+		/* get command line */
+		cmdline = pk_transaction_extra_get_cmdline (post, pid);
+		if (cmdline == NULL)
+			continue;
+
+		/* prepend path if it does not already exist */
+		if (cmdline[0] == '/')
+			cmdline_full = g_strdup (cmdline);
+		else
+			cmdline_full = g_strdup_printf ("/usr/bin/%s", cmdline);
+
+		egg_warning ("pid=%i: %s (%i)", pid, cmdline_full, uid);
+		if (uid < 500)
+			g_ptr_array_add (files_system, cmdline_full);
+		else
+			g_ptr_array_add (files_session, cmdline_full);
+		g_free (cmdline);
+	}
+
+	/* we found nothing */
+	if (files_system->len == 0 && files_session->len == 0) {
+		egg_warning ("no pids could be resolved");
+		goto out;
+	}
+
+	/* process all session restarts */
+	for (i=0; i<files_session->len; i++) {
+		filename = g_ptr_array_index (files_session, i);
+
+		obj = pk_transaction_extra_get_installed_package_for_file (post, filename);
+		if (obj == NULL) {
+			egg_warning ("failed to find package for %s", filename);
+			continue;
+		}
+
+		package_id = pk_package_id_to_string (obj->id);
+		pk_transaction_extra_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SESSION, package_id);
+		g_free (package_id);
+	}
+
+	/* process all system restarts */
+	for (i=0; i<files_system->len; i++) {
+		filename = g_ptr_array_index (files_system, i);
+
+		obj = pk_transaction_extra_get_installed_package_for_file (post, filename);
+		if (obj == NULL) {
+			egg_warning ("failed to find package for %s", filename);
+			continue;
+		}
+
+		package_id = pk_package_id_to_string (obj->id);
+		pk_transaction_extra_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SYSTEM, package_id);
+		g_free (package_id);
+	}
+
+out:
+	g_ptr_array_foreach (files_session, (GFunc) g_free, NULL);
+	g_ptr_array_foreach (files_system, (GFunc) g_free, NULL);
+	g_ptr_array_free (files_session, TRUE);
+	g_ptr_array_free (files_system, TRUE);
+	return TRUE;
+}
+
+/**
+ * pk_transaction_extra_check_library_restart:
+ **/
+gboolean
+pk_transaction_extra_check_library_restart (PkTransactionExtra *post, gchar **package_ids)
+{
+	PkStore *store;
+	guint signal_files;
+	gboolean ret = TRUE;
+	gchar **files = NULL;
+	GPtrArray *pids;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	if (post->priv->backend->desc->get_files == NULL) {
+		egg_debug ("cannot get files");
+		return FALSE;
+	}
+
+	/* reset */
+	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
+	g_ptr_array_set_size (post->priv->files_list, 0);
+
+	/* get list from lsof */
+	ret = pk_lsof_refresh (post->priv->lsof);
+	if (!ret) {
+		egg_warning ("failed to refresh");
+		goto out;
+	}
+
+	pk_transaction_extra_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+	pk_transaction_extra_set_progress_changed (post, 101);
+
+	store = pk_backend_get_store (post->priv->backend);
+	signal_files = g_signal_connect (post->priv->backend, "files",
+					 G_CALLBACK (pk_transaction_extra_files_check_library_restart_cb), post);
+
+	/* get all the files touched in the packages we just updated */
+	pk_backend_reset (post->priv->backend);
+	pk_store_set_strv (store, "package_ids", package_ids);
+	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	/* nothing to do */
+	if (post->priv->files_list->len == 0) {
+		egg_warning ("no files");
+		goto out;
+	}
+
+	/* get the list of PIDs */
+	files = pk_ptr_array_to_strv (post->priv->files_list);
+	pids = pk_lsof_get_pids_for_filenames (post->priv->lsof, files);
+
+	/* nothing depends on these libraries */
+	if (pids == NULL) {
+		egg_warning ("failed to get process list");
+		goto out;
+	}
+
+	/* nothing depends on these libraries */
+	if (pids->len == 0) {
+		egg_warning ("no processes depend on these libraries");
+		goto out;
+	}
+
+	/* emit */
+	pk_transaction_extra_check_library_restart_emit (post, pids);
+	g_ptr_array_free (pids, TRUE);
+
+	g_signal_handler_disconnect (post->priv->backend, signal_files);
+	pk_transaction_extra_set_progress_changed (post, 100);
+out:
+	g_strfreev (files);
+	return ret;
+}
+
+/**
+ * pk_transaction_extra_finalize:
+ **/
+static void
+pk_transaction_extra_finalize (GObject *object)
+{
+	PkTransactionExtra *post;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (PK_IS_POST_TRANS (object));
+	post = PK_POST_TRANS (object);
+
+	g_signal_handler_disconnect (post->priv->backend, post->priv->finished_id);
+	g_signal_handler_disconnect (post->priv->backend, post->priv->package_id);
+
+	if (g_main_loop_is_running (post->priv->loop))
+		g_main_loop_quit (post->priv->loop);
+	g_main_loop_unref (post->priv->loop);
+	sqlite3_close (post->priv->db);
+	g_hash_table_unref (post->priv->hash);
+	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
+	g_ptr_array_free (post->priv->files_list, TRUE);
+
+	g_object_unref (post->priv->backend);
+	g_object_unref (post->priv->lsof);
+	g_object_unref (post->priv->list);
+	g_object_unref (post->priv->running_exec_list);
+
+	G_OBJECT_CLASS (pk_transaction_extra_parent_class)->finalize (object);
+}
+
+/**
+ * pk_transaction_extra_class_init:
+ **/
+static void
+pk_transaction_extra_class_init (PkTransactionExtraClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = pk_transaction_extra_finalize;
+	signals [PK_POST_TRANS_STATUS_CHANGED] =
+		g_signal_new ("status-changed",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL, g_cclosure_marshal_VOID__UINT,
+			      G_TYPE_NONE, 1, G_TYPE_UINT);
+	signals [PK_POST_TRANS_PROGRESS_CHANGED] =
+		g_signal_new ("progress-changed",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL, pk_marshal_VOID__UINT_UINT_UINT_UINT,
+			      G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+	signals [PK_POST_TRANS_REQUIRE_RESTART] =
+		g_signal_new ("require-restart",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
+			      G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
+	g_type_class_add_private (klass, sizeof (PkTransactionExtraPrivate));
+}
+
+/**
+ * pk_transaction_extra_init:
+ *
+ * initializes the post_trans class. NOTE: We expect post_trans objects
+ * to *NOT* be removed or added during the session.
+ * We only control the first post_trans object if there are more than one.
+ **/
+static void
+pk_transaction_extra_init (PkTransactionExtra *post)
+{
+	gboolean ret;
+	const gchar *statement;
+	gchar *error_msg = NULL;
+	gint rc;
+
+	post->priv = PK_POST_TRANS_GET_PRIVATE (post);
+	post->priv->running_exec_list = pk_transaction_extra_string_list_new ();
+	post->priv->loop = g_main_loop_new (NULL, FALSE);
+	post->priv->list = pk_package_list_new ();
+	post->priv->backend = pk_backend_new ();
+	post->priv->lsof = pk_lsof_new ();
+	post->priv->db = NULL;
+	post->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	post->priv->files_list = g_ptr_array_new ();
+
+	post->priv->finished_id =
+		g_signal_connect (post->priv->backend, "finished",
+				  G_CALLBACK (pk_transaction_extra_finished_cb), post);
+	post->priv->package_id =
+		g_signal_connect (post->priv->backend, "package",
+				  G_CALLBACK (pk_transaction_extra_package_cb), post);
+
+	/* check if exists */
+	ret = g_file_test (PK_DESKTOP_DEFAULT_DATABASE, G_FILE_TEST_EXISTS);
+
+	egg_debug ("trying to open database '%s'", PK_DESKTOP_DEFAULT_DATABASE);
+	rc = sqlite3_open (PK_DESKTOP_DEFAULT_DATABASE, &post->priv->db);
+	if (rc != 0) {
+		egg_warning ("Can't open database: %s\n", sqlite3_errmsg (post->priv->db));
+		sqlite3_close (post->priv->db);
+		post->priv->db = NULL;
+		return;
+	}
+
+	/* create if not exists */
+	if (!ret) {
+		egg_debug ("creating database cache in %s", PK_DESKTOP_DEFAULT_DATABASE);
+		statement = "CREATE TABLE cache ("
+			    "filename TEXT,"
+			    "package TEXT,"
+			    "show INTEGER,"
+			    "md5 TEXT);";
+		rc = sqlite3_exec (post->priv->db, statement, NULL, NULL, &error_msg);
+		if (rc != SQLITE_OK) {
+			egg_warning ("SQL error: %s\n", error_msg);
+			sqlite3_free (error_msg);
+		}
+	}
+
+	/* we don't need to keep syncing */
+	sqlite3_exec (post->priv->db, "PRAGMA synchronous=OFF", NULL, NULL, NULL);
+}
+
+/**
+ * pk_transaction_extra_new:
+ * Return value: A new post_trans class instance.
+ **/
+PkTransactionExtra *
+pk_transaction_extra_new (void)
+{
+	PkTransactionExtra *post;
+	post = g_object_new (PK_TYPE_POST_TRANS, NULL);
+	return PK_POST_TRANS (post);
+}
+
+/***************************************************************************
+ ***                          MAKE CHECK TESTS                           ***
+ ***************************************************************************/
+#ifdef EGG_TEST
+#include "egg-test.h"
+
+void
+egg_test_post_trans (EggTest *test)
+{
+	PkTransactionExtra *post;
+
+	if (!egg_test_start (test, "PkTransactionExtra"))
+		return;
+
+	/************************************************************/
+	egg_test_title (test, "get an instance");
+	post = pk_transaction_extra_new ();
+	egg_test_assert (test, post != NULL);
+
+	g_object_unref (post);
+
+	egg_test_end (test);
+}
+#endif
+
diff --git a/src/pk-transaction-extra.h b/src/pk-transaction-extra.h
new file mode 100644
index 0000000..8a4160c
--- /dev/null
+++ b/src/pk-transaction-extra.h
@@ -0,0 +1,65 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2008-2009 Richard Hughes <richard at hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PK_POST_TRANS_H
+#define __PK_POST_TRANS_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PK_TYPE_POST_TRANS		(pk_transaction_extra_get_type ())
+#define PK_POST_TRANS(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), PK_TYPE_POST_TRANS, PkTransactionExtra))
+#define PK_POST_TRANS_CLASS(k)		(G_TYPE_CHECK_CLASS_CAST((k), PK_TYPE_POST_TRANS, PkTransactionExtraClass))
+#define PK_IS_POST_TRANS(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), PK_TYPE_POST_TRANS))
+#define PK_IS_POST_TRANS_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), PK_TYPE_POST_TRANS))
+#define PK_POST_TRANS_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), PK_TYPE_POST_TRANS, PkTransactionExtraClass))
+
+typedef struct PkTransactionExtraPrivate PkTransactionExtraPrivate;
+
+typedef struct
+{
+	GObject		      parent;
+	PkTransactionExtraPrivate     *priv;
+} PkTransactionExtra;
+
+typedef struct
+{
+	GObjectClass	parent_class;
+} PkTransactionExtraClass;
+
+GType		 pk_transaction_extra_get_type			(void);
+PkTransactionExtra	*pk_transaction_extra_new		(void);
+
+gboolean	 pk_transaction_extra_clear_firmware_requests	(PkTransactionExtra	*post);
+gboolean	 pk_transaction_extra_update_package_list	(PkTransactionExtra	*post);
+gboolean	 pk_transaction_extra_import_desktop_files	(PkTransactionExtra	*post);
+gboolean	 pk_transaction_extra_check_running_process	(PkTransactionExtra	*post,
+								 gchar			**package_ids);
+gboolean	 pk_transaction_extra_check_desktop_files	(PkTransactionExtra	*post,
+								 gchar			**package_ids);
+gboolean	 pk_transaction_extra_check_library_restart	(PkTransactionExtra	*post,
+								 gchar			**package_ids);
+
+G_END_DECLS
+
+#endif /* __PK_POST_TRANS_H */
+
diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index b1400f2..0aa4e2a 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -61,7 +61,7 @@
 #include "pk-shared.h"
 #include "pk-cache.h"
 #include "pk-notify.h"
-#include "pk-post-trans.h"
+#include "pk-transaction-extra.h"
 #include "pk-syslog.h"
 
 static void     pk_transaction_finalize		(GObject	    *object);
@@ -107,7 +107,7 @@ struct PkTransactionPrivate
 #endif
 	DBusGConnection		*connection;
 	DBusGProxy		*proxy_pid;
-	PkPostTrans		*post_trans;
+	PkTransactionExtra		*transaction_extra;
 	PkSyslog		*syslog;
 
 	/* needed for gui coldplugging */
@@ -610,7 +610,7 @@ pk_transaction_finished_cb (PkBackend *backend, PkExitEnum exit_enum, PkTransact
 			/* process file lists on these packages */
 			if (list->len > 0) {
 				package_ids = pk_package_ids_from_array (list);
-				pk_post_trans_check_running_process (transaction->priv->post_trans, package_ids);
+				pk_transaction_extra_check_running_process (transaction->priv->transaction_extra, package_ids);
 				g_strfreev (package_ids);
 			}
 			g_ptr_array_foreach (list, (GFunc) g_free, NULL);
@@ -642,7 +642,7 @@ pk_transaction_finished_cb (PkBackend *backend, PkExitEnum exit_enum, PkTransact
 			/* process file lists on these packages */
 			if (list->len > 0) {
 				package_ids = pk_package_ids_from_array (list);
-				pk_post_trans_check_desktop_files (transaction->priv->post_trans, package_ids);
+				pk_transaction_extra_check_desktop_files (transaction->priv->transaction_extra, package_ids);
 				g_strfreev (package_ids);
 			}
 			g_ptr_array_foreach (list, (GFunc) g_free, NULL);
@@ -664,15 +664,15 @@ pk_transaction_finished_cb (PkBackend *backend, PkExitEnum exit_enum, PkTransact
 		/* generate the package list */
 		ret = pk_conf_get_bool (transaction->priv->conf, "UpdatePackageList");
 		if (ret)
-			pk_post_trans_update_package_list (transaction->priv->post_trans);
+			pk_transaction_extra_update_package_list (transaction->priv->transaction_extra);
 
 		/* refresh the desktop icon cache */
 		ret = pk_conf_get_bool (transaction->priv->conf, "ScanDesktopFiles");
 		if (ret)
-			pk_post_trans_import_desktop_files (transaction->priv->post_trans);
+			pk_transaction_extra_import_desktop_files (transaction->priv->transaction_extra);
 
 		/* clear the firmware requests directory */
-		pk_post_trans_clear_firmware_requests (transaction->priv->post_trans);
+		pk_transaction_extra_clear_firmware_requests (transaction->priv->transaction_extra);
 	}
 
 	/* if we did not send this, ensure the GUI has the right state */
@@ -1111,7 +1111,7 @@ pk_transaction_pre_transaction_checks (PkTransaction *transaction)
 
 	/* find files in security updates */
 	package_ids = pk_package_ids_from_array (list);
-	ret = pk_post_trans_check_library_restart (transaction->priv->post_trans, package_ids);
+	ret = pk_transaction_extra_check_library_restart (transaction->priv->transaction_extra, package_ids);
 out:
 	g_strfreev (package_ids);
 	if (list != NULL) {
@@ -4312,12 +4312,12 @@ pk_transaction_init (PkTransaction *transaction)
 	transaction->priv->cancellable = g_cancellable_new ();
 #endif
 
-	transaction->priv->post_trans = pk_post_trans_new ();
-	g_signal_connect (transaction->priv->post_trans, "status-changed",
+	transaction->priv->transaction_extra = pk_transaction_extra_new ();
+	g_signal_connect (transaction->priv->transaction_extra, "status-changed",
 			  G_CALLBACK (pk_transaction_status_changed_cb), transaction);
-	g_signal_connect (transaction->priv->post_trans, "progress-changed",
+	g_signal_connect (transaction->priv->transaction_extra, "progress-changed",
 			  G_CALLBACK (pk_transaction_progress_changed_cb), transaction);
-	g_signal_connect (transaction->priv->post_trans, "require-restart",
+	g_signal_connect (transaction->priv->transaction_extra, "require-restart",
 			  G_CALLBACK (pk_transaction_require_restart_cb), transaction);
 
 	transaction->priv->transaction_db = pk_transaction_db_new ();
@@ -4418,7 +4418,7 @@ pk_transaction_finalize (GObject *object)
 	g_object_unref (transaction->priv->proxy_pid);
 	g_object_unref (transaction->priv->notify);
 	g_object_unref (transaction->priv->syslog);
-	g_object_unref (transaction->priv->post_trans);
+	g_object_unref (transaction->priv->transaction_extra);
 #ifdef USE_SECURITY_POLKIT
 //	g_object_unref (transaction->priv->authority);
 	g_object_unref (transaction->priv->cancellable);
commit 79b00617cf9808a1b61448354403d325820e6ea3
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 17:02:50 2009 +0100

    Add functionality to detect when shared libraries are being used that are updated in a security update

diff --git a/src/pk-post-trans.c b/src/pk-post-trans.c
index 52556a5..3021ff1 100644
--- a/src/pk-post-trans.c
+++ b/src/pk-post-trans.c
@@ -38,6 +38,7 @@
 #include "pk-shared.h"
 #include "pk-marshal.h"
 #include "pk-backend-internal.h"
+#include "pk-lsof.h"
 
 #define PK_POST_TRANS_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_POST_TRANS, PkPostTransPrivate))
 
@@ -48,14 +49,17 @@ struct PkPostTransPrivate
 	GMainLoop		*loop;
 	PkObjList		*running_exec_list;
 	PkPackageList		*list;
+	PkLsof			*lsof;
 	guint			 finished_id;
 	guint			 package_id;
 	GHashTable		*hash;
+	GPtrArray		*files_list;
 };
 
 enum {
 	PK_POST_TRANS_STATUS_CHANGED,
 	PK_POST_TRANS_PROGRESS_CHANGED,
+	PK_POST_TRANS_REQUIRE_RESTART,
 	PK_POST_TRANS_LAST_SIGNAL
 };
 
@@ -82,6 +86,16 @@ pk_post_trans_package_cb (PkBackend *backend, const PkPackageObj *obj, PkPostTra
 }
 
 /**
+ * pk_post_trans_set_require_restart:
+ **/
+static void
+pk_post_trans_set_require_restart (PkPostTrans *post, PkRestartEnum restart, const gchar *package_id)
+{
+	egg_debug ("emit require-restart %s, %s", pk_restart_enum_to_text (restart), package_id);
+	g_signal_emit (post, signals [PK_POST_TRANS_REQUIRE_RESTART], 0, restart, package_id);
+}
+
+/**
  * pk_post_trans_set_status_changed:
  **/
 static void
@@ -737,6 +751,260 @@ pk_post_trans_check_desktop_files (PkPostTrans *post, gchar **package_ids)
 }
 
 /**
+ * pk_post_trans_files_check_library_restart_cb:
+ **/
+static void
+pk_post_trans_files_check_library_restart_cb (PkBackend *backend, const gchar *package_id,
+					      const gchar *filelist, PkPostTrans *post)
+{
+	guint i;
+	guint len;
+	gchar **files = NULL;
+
+	files = g_strsplit (filelist, ";", 0);
+
+	/* check each file to see if it's a system shared library */
+	len = g_strv_length (files);
+	for (i=0; i<len; i++) {
+		/* not a system library */
+		if (strstr (files[i], "/lib/") == NULL)
+			continue;
+
+		/* not a shared object */
+		if (strstr (files[i], ".so") == NULL)
+			continue;
+
+		/* add as it matches the criteria */
+		egg_debug ("adding filename %s", files[i]);
+		g_ptr_array_add (post->priv->files_list, g_strdup (files[i]));
+	}
+}
+
+/**
+ * pk_post_trans_get_cmdline:
+ **/
+static gchar *
+pk_post_trans_get_cmdline (PkPostTrans *post, guint pid)
+{
+	gboolean ret;
+	gchar *filename = NULL;
+	gchar *cmdline = NULL;
+	GError *error = NULL;
+
+	/* get command line from proc */
+	filename = g_strdup_printf ("/proc/%i/cmdline", pid);
+	ret = g_file_get_contents (filename, &cmdline, NULL, &error);
+	if (!ret) {
+		egg_warning ("failed to get cmdline: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+out:
+	g_free (filename);
+	return cmdline;
+}
+
+/**
+ * pk_post_trans_get_uid:
+ **/
+static gint
+pk_post_trans_get_uid (PkPostTrans *post, guint pid)
+{
+	gboolean ret;
+	gint uid = -1;
+	gchar *filename = NULL;
+	gchar *uid_text = NULL;
+	GError *error = NULL;
+
+	/* get command line from proc */
+	filename = g_strdup_printf ("/proc/%i/loginuid", pid);
+	ret = g_file_get_contents (filename, &uid_text, NULL, &error);
+	if (!ret) {
+		egg_warning ("failed to get cmdline: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* convert from text */
+	uid = atoi (uid_text);
+out:
+	g_free (filename);
+	g_free (uid_text);
+	return uid;
+}
+
+/**
+ * pk_post_trans_check_library_restart_emit:
+ **/
+static gboolean
+pk_post_trans_check_library_restart_emit (PkPostTrans *post, GPtrArray *pids)
+{
+	gint uid;
+	guint i;
+	guint pid;
+	gchar *filename;
+	gchar *cmdline;
+	gchar *cmdline_full;
+	gchar *package_id;
+	GPtrArray *files_session;
+	GPtrArray *files_system;
+	const PkPackageObj *obj;
+
+	/* create arrays */
+	files_session = g_ptr_array_new ();
+	files_system = g_ptr_array_new ();
+
+	/* find the package name of each pid */
+	for (i=0; i<pids->len; i++) {
+		pid = GPOINTER_TO_INT (g_ptr_array_index (pids, i));
+
+		/* get user */
+		uid = pk_post_trans_get_uid (post, pid);
+		if (uid < 0)
+			continue;
+
+		/* get command line */
+		cmdline = pk_post_trans_get_cmdline (post, pid);
+		if (cmdline == NULL)
+			continue;
+
+		/* prepend path if it does not already exist */
+		if (cmdline[0] == '/')
+			cmdline_full = g_strdup (cmdline);
+		else
+			cmdline_full = g_strdup_printf ("/usr/bin/%s", cmdline);
+
+		egg_warning ("pid=%i: %s (%i)", pid, cmdline_full, uid);
+		if (uid < 500)
+			g_ptr_array_add (files_system, cmdline_full);
+		else
+			g_ptr_array_add (files_session, cmdline_full);
+		g_free (cmdline);
+	}
+
+	/* we found nothing */
+	if (files_system->len == 0 && files_session->len == 0) {
+		egg_warning ("no pids could be resolved");
+		goto out;
+	}
+
+	/* process all session restarts */
+	for (i=0; i<files_session->len; i++) {
+		filename = g_ptr_array_index (files_session, i);
+
+		obj = pk_post_trans_get_installed_package_for_file (post, filename);
+		if (obj == NULL) {
+			egg_warning ("failed to find package for %s", filename);
+			continue;
+		}
+
+		package_id = pk_package_id_to_string (obj->id);
+		pk_post_trans_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SESSION, package_id);
+		g_free (package_id);
+	}
+
+	/* process all system restarts */
+	for (i=0; i<files_system->len; i++) {
+		filename = g_ptr_array_index (files_system, i);
+
+		obj = pk_post_trans_get_installed_package_for_file (post, filename);
+		if (obj == NULL) {
+			egg_warning ("failed to find package for %s", filename);
+			continue;
+		}
+
+		package_id = pk_package_id_to_string (obj->id);
+		pk_post_trans_set_require_restart (post, PK_RESTART_ENUM_SECURITY_SYSTEM, package_id);
+		g_free (package_id);
+	}
+
+out:
+	g_ptr_array_foreach (files_session, (GFunc) g_free, NULL);
+	g_ptr_array_foreach (files_system, (GFunc) g_free, NULL);
+	g_ptr_array_free (files_session, TRUE);
+	g_ptr_array_free (files_system, TRUE);
+	return TRUE;
+}
+
+/**
+ * pk_post_trans_check_library_restart:
+ **/
+gboolean
+pk_post_trans_check_library_restart (PkPostTrans *post, gchar **package_ids)
+{
+	PkStore *store;
+	guint signal_files;
+	gboolean ret = TRUE;
+	gchar **files = NULL;
+	GPtrArray *pids;
+
+	g_return_val_if_fail (PK_IS_POST_TRANS (post), FALSE);
+
+	if (post->priv->backend->desc->get_files == NULL) {
+		egg_debug ("cannot get files");
+		return FALSE;
+	}
+
+	/* reset */
+	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
+	g_ptr_array_set_size (post->priv->files_list, 0);
+
+	/* get list from lsof */
+	ret = pk_lsof_refresh (post->priv->lsof);
+	if (!ret) {
+		egg_warning ("failed to refresh");
+		goto out;
+	}
+
+	pk_post_trans_set_status_changed (post, PK_STATUS_ENUM_SCAN_APPLICATIONS);
+	pk_post_trans_set_progress_changed (post, 101);
+
+	store = pk_backend_get_store (post->priv->backend);
+	signal_files = g_signal_connect (post->priv->backend, "files",
+					 G_CALLBACK (pk_post_trans_files_check_library_restart_cb), post);
+
+	/* get all the files touched in the packages we just updated */
+	pk_backend_reset (post->priv->backend);
+	pk_store_set_strv (store, "package_ids", package_ids);
+	post->priv->backend->desc->get_files (post->priv->backend, package_ids);
+
+	/* wait for finished */
+	g_main_loop_run (post->priv->loop);
+
+	/* nothing to do */
+	if (post->priv->files_list->len == 0) {
+		egg_warning ("no files");
+		goto out;
+	}
+
+	/* get the list of PIDs */
+	files = pk_ptr_array_to_strv (post->priv->files_list);
+	pids = pk_lsof_get_pids_for_filenames (post->priv->lsof, files);
+
+	/* nothing depends on these libraries */
+	if (pids == NULL) {
+		egg_warning ("failed to get process list");
+		goto out;
+	}
+
+	/* nothing depends on these libraries */
+	if (pids->len == 0) {
+		egg_warning ("no processes depend on these libraries");
+		goto out;
+	}
+
+	/* emit */
+	pk_post_trans_check_library_restart_emit (post, pids);
+	g_ptr_array_free (pids, TRUE);
+
+	g_signal_handler_disconnect (post->priv->backend, signal_files);
+	pk_post_trans_set_progress_changed (post, 100);
+out:
+	g_strfreev (files);
+	return ret;
+}
+
+/**
  * pk_post_trans_finalize:
  **/
 static void
@@ -756,8 +1024,11 @@ pk_post_trans_finalize (GObject *object)
 	g_main_loop_unref (post->priv->loop);
 	sqlite3_close (post->priv->db);
 	g_hash_table_unref (post->priv->hash);
+	g_ptr_array_foreach (post->priv->files_list, (GFunc) g_free, NULL);
+	g_ptr_array_free (post->priv->files_list, TRUE);
 
 	g_object_unref (post->priv->backend);
+	g_object_unref (post->priv->lsof);
 	g_object_unref (post->priv->list);
 	g_object_unref (post->priv->running_exec_list);
 
@@ -782,6 +1053,11 @@ pk_post_trans_class_init (PkPostTransClass *klass)
 			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
 			      0, NULL, NULL, pk_marshal_VOID__UINT_UINT_UINT_UINT,
 			      G_TYPE_NONE, 4, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+	signals [PK_POST_TRANS_REQUIRE_RESTART] =
+		g_signal_new ("require-restart",
+			      G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST,
+			      0, NULL, NULL, pk_marshal_VOID__UINT_STRING,
+			      G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_STRING);
 	g_type_class_add_private (klass, sizeof (PkPostTransPrivate));
 }
 
@@ -805,8 +1081,10 @@ pk_post_trans_init (PkPostTrans *post)
 	post->priv->loop = g_main_loop_new (NULL, FALSE);
 	post->priv->list = pk_package_list_new ();
 	post->priv->backend = pk_backend_new ();
+	post->priv->lsof = pk_lsof_new ();
 	post->priv->db = NULL;
 	post->priv->hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+	post->priv->files_list = g_ptr_array_new ();
 
 	post->priv->finished_id =
 		g_signal_connect (post->priv->backend, "finished",
diff --git a/src/pk-post-trans.h b/src/pk-post-trans.h
index 66abd14..7f2fe9f 100644
--- a/src/pk-post-trans.h
+++ b/src/pk-post-trans.h
@@ -56,6 +56,8 @@ gboolean	 pk_post_trans_check_running_process	(PkPostTrans	*post,
 							 gchar		**package_ids);
 gboolean	 pk_post_trans_check_desktop_files	(PkPostTrans	*post,
 							 gchar		**package_ids);
+gboolean	 pk_post_trans_check_library_restart	(PkPostTrans	*post,
+							 gchar		**package_ids);
 
 G_END_DECLS
 
diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index 2b451e8..b1400f2 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -1059,11 +1059,75 @@ pk_transaction_update_detail_cb (PkBackend *backend, const PkUpdateDetailObj *de
 }
 
 /**
+ * pk_transaction_pre_transaction_checks:
+ *
+ * TODO: also check if package in InstallPackages is in the update lists
+ *       and do the same check if this is so.
+ */
+static gboolean
+pk_transaction_pre_transaction_checks (PkTransaction *transaction)
+{
+	PkPackageList *updates;
+	const PkPackageObj *obj;
+	guint i;
+	guint length;
+	gboolean ret = FALSE;
+	gchar *package_id;
+	gchar **package_ids = NULL;
+	GPtrArray *list = NULL;
+
+	/* only do this for update actions */
+	if (transaction->priv->role != PK_ROLE_ENUM_UPDATE_SYSTEM &&
+	    transaction->priv->role != PK_ROLE_ENUM_UPDATE_PACKAGES) {
+		egg_debug ("doing nothing, as not update");
+		goto out;
+	}
+
+	/* do we want to enable this codepath? */
+	ret = pk_conf_get_bool (transaction->priv->conf, "CheckSharedLibrariesInUse");
+	if (!ret) {
+		egg_warning ("not checking for library restarts");
+		goto out;
+	}
+
+	/* do we have a cache */
+	updates = pk_cache_get_updates (transaction->priv->cache);
+	if (updates == NULL) {
+		egg_warning ("no updates cache");
+		goto out;
+	}
+
+	/* find security update packages */
+	list = g_ptr_array_new ();
+	length = pk_package_list_get_size (updates);
+	for (i=0; i<length; i++) {
+		obj = pk_package_list_get_obj (updates, i);
+		if (obj->info == PK_INFO_ENUM_SECURITY) {
+			package_id = pk_package_id_to_string (obj->id);
+			egg_debug ("security update: %s", package_id);
+			g_ptr_array_add (list, package_id);
+		}
+	}
+
+	/* find files in security updates */
+	package_ids = pk_package_ids_from_array (list);
+	ret = pk_post_trans_check_library_restart (transaction->priv->post_trans, package_ids);
+out:
+	g_strfreev (package_ids);
+	if (list != NULL) {
+		g_ptr_array_foreach (list, (GFunc) g_free, NULL);
+		g_ptr_array_free (list, TRUE);
+	}
+	return ret;
+}
+
+/**
  * pk_transaction_set_running:
  */
 G_GNUC_WARN_UNUSED_RESULT static gboolean
 pk_transaction_set_running (PkTransaction *transaction)
 {
+	gboolean ret;
 	PkBackendDesc *desc;
 	PkStore *store;
 	PkTransactionPrivate *priv = PK_TRANSACTION_GET_PRIVATE (transaction);
@@ -1092,6 +1156,13 @@ pk_transaction_set_running (PkTransaction *transaction)
 	pk_backend_set_role (priv->backend, priv->role);
 	egg_debug ("setting role for %s to %s", priv->tid, pk_role_enum_to_text (priv->role));
 
+	/* do any pre transaction checks */
+	ret = pk_transaction_pre_transaction_checks (transaction);
+
+	/* might have to reset again if we used the backend */
+	if (ret)
+		pk_backend_reset (transaction->priv->backend);
+
 	/* connect up the signals */
 	transaction->priv->signal_allow_cancel =
 		g_signal_connect (transaction->priv->backend, "allow-cancel",
@@ -4246,6 +4317,8 @@ pk_transaction_init (PkTransaction *transaction)
 			  G_CALLBACK (pk_transaction_status_changed_cb), transaction);
 	g_signal_connect (transaction->priv->post_trans, "progress-changed",
 			  G_CALLBACK (pk_transaction_progress_changed_cb), transaction);
+	g_signal_connect (transaction->priv->post_trans, "require-restart",
+			  G_CALLBACK (pk_transaction_require_restart_cb), transaction);
 
 	transaction->priv->transaction_db = pk_transaction_db_new ();
 	g_signal_connect (transaction->priv->transaction_db, "transaction",
commit 6e38b8aa7a45dbeb117eabf5a427526bd00562cd
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 17:01:57 2009 +0100

    Add the new restart enums into pkcon

diff --git a/client/pk-console.c b/client/pk-console.c
index 43a32ca..be0f167 100644
--- a/client/pk-console.c
+++ b/client/pk-console.c
@@ -518,6 +518,12 @@ pk_console_require_restart_cb (PkClient *client, const PkRequireRestartObj *obj,
 	} else if (obj->restart == PK_RESTART_ENUM_SESSION) {
 		/* TRANSLATORS: a package requires the session to be restarted */
 		g_print ("%s %s-%s.%s\n", _("Session restart required:"), obj->id->name, obj->id->version, obj->id->arch);
+	} else if (obj->restart == PK_RESTART_ENUM_SECURITY_SYSTEM) {
+		/* TRANSLATORS: a package requires the system to be restarted due to a security update*/
+		g_print ("%s %s-%s.%s\n", _("System restart (security) required by:"), obj->id->name, obj->id->version, obj->id->arch);
+	} else if (obj->restart == PK_RESTART_ENUM_SECURITY_SESSION) {
+		/* TRANSLATORS: a package requires the session to be restarted due to a security update */
+		g_print ("%s %s-%s.%s\n", _("Session restart (security) required:"), obj->id->name, obj->id->version, obj->id->arch);
 	} else if (obj->restart == PK_RESTART_ENUM_APPLICATION) {
 		/* TRANSLATORS: a package requires the application to be restarted */
 		g_print ("%s %s-%s.%s\n", _("Application restart required by:"), obj->id->name, obj->id->version, obj->id->arch);
@@ -574,7 +580,7 @@ pk_console_finished_cb (PkClient *client, PkExitEnum exit_enum, guint runtime, g
 	/* is there any restart to notify the user? */
 	restart = pk_client_get_require_restart (client);
 	if (restart == PK_RESTART_ENUM_SYSTEM) {
-		/* TRANSLATORS: a package needs to restart they system */
+		/* TRANSLATORS: a package needs to restart their system */
 		g_print ("%s\n", _("Please restart the computer to complete the update."));
 	} else if (restart == PK_RESTART_ENUM_SESSION) {
 		/* TRANSLATORS: a package needs to restart the session */
@@ -582,6 +588,12 @@ pk_console_finished_cb (PkClient *client, PkExitEnum exit_enum, guint runtime, g
 	} else if (restart == PK_RESTART_ENUM_APPLICATION) {
 		/* TRANSLATORS: a package needs to restart the application */
 		g_print ("%s\n", _("Please restart the application as it is being used."));
+	} else if (restart == PK_RESTART_ENUM_SECURITY_SYSTEM) {
+		/* TRANSLATORS: a package needs to restart their system (due to security) */
+		g_print ("%s\n", _("Please restart the computer to complete the update as important security updates have been installed."));
+	} else if (restart == PK_RESTART_ENUM_SECURITY_SESSION) {
+		/* TRANSLATORS: a package needs to restart the session (due to security) */
+		g_print ("%s\n", _("Please logout and login to complete the update as important security updates have been installed."));
 	}
 
 	/* need to handle retry with only_trusted=FALSE */
commit 5de0bfac9a63f520b5ff4175012222d7c7aa9af4
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 16:54:32 2009 +0100

    Add two more require-restart constants needed for future code

diff --git a/lib/packagekit-glib/pk-enum.c b/lib/packagekit-glib/pk-enum.c
index 806e9c6..bd47602 100644
--- a/lib/packagekit-glib/pk-enum.c
+++ b/lib/packagekit-glib/pk-enum.c
@@ -190,6 +190,8 @@ static const PkEnumMatch enum_restart[] = {
 	{PK_RESTART_ENUM_SYSTEM,		"system"},
 	{PK_RESTART_ENUM_SESSION,		"session"},
 	{PK_RESTART_ENUM_APPLICATION,		"application"},
+	{PK_RESTART_ENUM_SECURITY_SYSTEM,	"security-system"},
+	{PK_RESTART_ENUM_SECURITY_SESSION,	"security-session"},
 	{0, NULL}
 };
 
diff --git a/lib/packagekit-glib/pk-enum.h b/lib/packagekit-glib/pk-enum.h
index 9bf6c48..927a9dd 100644
--- a/lib/packagekit-glib/pk-enum.h
+++ b/lib/packagekit-glib/pk-enum.h
@@ -205,13 +205,15 @@ typedef enum {
 /**
  * PkRestartEnum:
  *
- * What restart we need to after a transaction
+ * What restart we need to after a transaction, ordered by severity
  **/
 typedef enum {
 	PK_RESTART_ENUM_NONE,
 	PK_RESTART_ENUM_APPLICATION,
 	PK_RESTART_ENUM_SESSION,
 	PK_RESTART_ENUM_SYSTEM,
+	PK_RESTART_ENUM_SECURITY_SESSION,	/* a library that is being used by this package has been updated for security */
+	PK_RESTART_ENUM_SECURITY_SYSTEM,
 	PK_RESTART_ENUM_UNKNOWN
 } PkRestartEnum;
 
commit 1551df1bb3b319b6fbe3da34d98e8a1fee7fdbae
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 16:26:38 2009 +0100

    Add a config file entry for future shared library use

diff --git a/etc/PackageKit.conf.in b/etc/PackageKit.conf.in
index aba3b06..259a752 100644
--- a/etc/PackageKit.conf.in
+++ b/etc/PackageKit.conf.in
@@ -116,6 +116,15 @@ UpdatePackageList=true
 # default=true
 UpdateCheckProcesses=true
 
+# Check for shared libraries that are in use, that are replaced by packages
+# that are marked as security updates.
+#
+# NOTE: Don't enable this for backends that are slow doing GetFiles() on
+# installed files.
+#
+# default=true
+CheckSharedLibrariesInUse=true
+
 # Check for updates in testing repositories when we check for updates
 #
 # NOTE: Don't enable this if you do not want testing updates to be checked
commit 0b849cb9e39cc11d145312629399da822614ce72
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 16:24:56 2009 +0100

    use lsof -Fpfn which is designed for machines to read

diff --git a/src/pk-lsof.c b/src/pk-lsof.c
index 5130728..1211cc9 100644
--- a/src/pk-lsof.c
+++ b/src/pk-lsof.c
@@ -94,6 +94,43 @@ pk_lsof_data_new (guint pid, const gchar *filename)
 	return data;
 }
 
+typedef enum {
+	PK_LSOF_TYPE_MEM,
+	PK_LSOF_TYPE_DEL,
+	PK_LSOF_TYPE_TXT,
+	PK_LSOF_TYPE_UNKNOWN
+} PkLsofType;
+
+/**
+ * pk_lsof_type_to_text:
+ **/
+static const gchar *
+pk_lsof_type_to_text (PkLsofType type)
+{
+	if (type == PK_LSOF_TYPE_MEM)
+		return "mem";
+	if (type == PK_LSOF_TYPE_TXT)
+		return "txt";
+	if (type == PK_LSOF_TYPE_DEL)
+		return "del";
+	return "unknown";
+}
+
+/**
+ * pk_lsof_type_from_text:
+ **/
+static PkLsofType
+pk_lsof_type_from_text (const gchar *type)
+{
+	if (g_ascii_strcasecmp (type, "mem") == 0)
+		return PK_LSOF_TYPE_MEM;
+	if (g_ascii_strcasecmp (type, "txt") == 0)
+		return PK_LSOF_TYPE_TXT;
+	if (g_ascii_strcasecmp (type, "del") == 0)
+		return PK_LSOF_TYPE_DEL;
+	return PK_LSOF_TYPE_UNKNOWN;
+}
+
 /**
  * pk_lsof_refresh:
  **/
@@ -107,14 +144,15 @@ pk_lsof_refresh (PkLsof *lsof)
 	PkLsofData *data;
 	gchar **lines = NULL;
 	guint i;
-	const gchar *pid_text;
-	const gchar *type;
-	const gchar *filename;
+	const gchar *value;
+	gchar mode;
+	gint pid = -1;
+	PkLsofType type = PK_LSOF_TYPE_UNKNOWN;
 
 	g_return_val_if_fail (PK_IS_LSOF (lsof), FALSE);
 
 	/* run lsof to get all data */
-	ret = g_spawn_command_line_sync ("/usr/sbin/lsof", &stdout, &stderr, NULL, &error);
+	ret = g_spawn_command_line_sync ("/usr/sbin/lsof -Fpfn", &stdout, &stderr, NULL, &error);
 	if (!ret) {
 		egg_warning ("failed to get pids: %s", error->message);
 		g_error_free (error);
@@ -129,28 +167,40 @@ pk_lsof_refresh (PkLsof *lsof)
 	lines = g_strsplit (stdout, "\n", -1);
 	for (i=0; lines[i] != NULL; i++) {
 
-		/* parse: devhelp   11328   hughsie  mem       REG        8,2    65840  161702 /usr/lib/gio/modules/libgioremote-volume-monitor.so */
-		lines[i][17] = '\0';
-		lines[i][36] = '\0';
-		pid_text = &lines[i][10];
-		type = &lines[i][27];
-		filename = &lines[i][69+3];
-
-		/* only add memory mapped entries */
-		if (!g_str_has_prefix (type, "mem"))
+		/* get mode */
+		mode = lines[i][0];
+		if (mode == '\0')
 			continue;
 
-		/* not a system library */
-		if (strstr (filename, "/lib/") == NULL)
-			continue;
+		value = &lines[i][1];
+		switch (mode) {
+		case 'p':
+			pid = atoi (value);
+			break;
+		case 'f':
+			type = pk_lsof_type_from_text (value);
+			break;
+		case 'n':
+			if (type == PK_LSOF_TYPE_DEL ||
+			    type == PK_LSOF_TYPE_MEM) {
 
-		/* not a shared object */
-		if (strstr (filename, ".so") == NULL)
-			continue;
+				/* not a system library */
+				if (strstr (value, "/lib/") == NULL)
+					break;
 
-		/* add to array */
-		data = pk_lsof_data_new (atoi (pid_text), filename);
-		g_ptr_array_add (lsof->priv->list_data, data);
+				/* not a shared object */
+				if (strstr (value, ".so") == NULL)
+					break;
+
+				/* add to array */
+				data = pk_lsof_data_new (pid, value);
+				g_ptr_array_add (lsof->priv->list_data, data);
+			}
+			break;
+		default:
+			egg_debug ("ignoring %c=%s (type=%s)", mode, value, pk_lsof_type_to_text (type));
+			break;
+		}
 	}
 out:
 	g_strfreev (lines);
commit 4d2e71a7079d00870e6af1e436872655eababc21
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 15:27:10 2009 +0100

    Prevent duplicate require-restart signal from being sent from the daemon

diff --git a/src/pk-transaction.c b/src/pk-transaction.c
index 1bfa076..2b451e8 100644
--- a/src/pk-transaction.c
+++ b/src/pk-transaction.c
@@ -115,6 +115,7 @@ struct PkTransactionPrivate
 	gchar			*tid;
 	gchar			*sender;
 	gchar			*cmdline;
+	GPtrArray		*require_restart_list;
 	PkPackageList		*package_list;
 	PkTransactionList	*transaction_list;
 	PkTransactionDb		*transaction_db;
@@ -957,11 +958,35 @@ static void
 pk_transaction_require_restart_cb (PkBackend *backend, PkRestartEnum restart, const gchar *package_id, PkTransaction *transaction)
 {
 	const gchar *restart_text;
+	const gchar *package_id_tmp;
+	GPtrArray *list;
+	gboolean found = FALSE;
+	guint i;
 
 	g_return_if_fail (PK_IS_TRANSACTION (transaction));
 	g_return_if_fail (transaction->priv->tid != NULL);
 
 	restart_text = pk_restart_enum_to_text (restart);
+
+	/* filter out duplicates */
+	list = transaction->priv->require_restart_list;
+	for (i=0; i<list->len; i++) {
+		package_id_tmp = g_ptr_array_index (list, i);
+		if (g_strcmp0 (package_id, package_id_tmp) == 0) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	/* ignore */
+	if (found) {
+		egg_debug ("ignoring %s (%s) as already sent", restart_text, package_id);
+		return;
+	}
+
+	/* add to duplicate list */
+	g_ptr_array_add (list, g_strdup (package_id));
+
 	egg_debug ("emitting require-restart %s, '%s'", restart_text, package_id);
 	g_signal_emit (transaction, signals [PK_TRANSACTION_REQUIRE_RESTART], 0, restart_text, package_id);
 }
@@ -1045,6 +1070,10 @@ pk_transaction_set_running (PkTransaction *transaction)
 	g_return_val_if_fail (PK_IS_TRANSACTION (transaction), FALSE);
 	g_return_val_if_fail (transaction->priv->tid != NULL, FALSE);
 
+	/* reset the require-restart list */
+	g_ptr_array_foreach (transaction->priv->require_restart_list, (GFunc) g_free, NULL);
+	g_ptr_array_set_size (transaction->priv->require_restart_list, 0);
+
 	/* prepare for use; the transaction list ensures this is safe */
 	pk_backend_reset (transaction->priv->backend);
 
@@ -4198,6 +4227,7 @@ pk_transaction_init (PkTransaction *transaction)
 	transaction->priv->subpercentage = PK_BACKEND_PERCENTAGE_INVALID;
 	transaction->priv->elapsed = 0;
 	transaction->priv->remaining = 0;
+	transaction->priv->require_restart_list = g_ptr_array_new ();
 	transaction->priv->backend = pk_backend_new ();
 	transaction->priv->cache = pk_cache_new ();
 	transaction->priv->conf = pk_conf_new ();
@@ -4286,6 +4316,9 @@ pk_transaction_finalize (GObject *object)
 		g_object_unref (transaction->priv->subject);
 #endif
 
+	g_ptr_array_foreach (transaction->priv->require_restart_list, (GFunc) g_free, NULL);
+	g_ptr_array_free (transaction->priv->require_restart_list, TRUE);
+
 	g_free (transaction->priv->last_package_id);
 	g_free (transaction->priv->locale);
 	g_free (transaction->priv->cached_package_id);
commit f96ab45df8458eea676c030942555a20b8e97809
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 15:20:27 2009 +0100

    trivial: make pk_post_trans_get_installed_package_for_file() more generic

diff --git a/src/pk-post-trans.c b/src/pk-post-trans.c
index 4575c53..52556a5 100644
--- a/src/pk-post-trans.c
+++ b/src/pk-post-trans.c
@@ -102,14 +102,13 @@ pk_post_trans_set_progress_changed (PkPostTrans *post, guint percentage)
 }
 
 /**
- * pk_post_trans_import_desktop_files_get_package:
+ * pk_post_trans_get_installed_package_for_file:
  **/
-static gchar *
-pk_post_trans_import_desktop_files_get_package (PkPostTrans *post, const gchar *filename)
+static const PkPackageObj *
+pk_post_trans_get_installed_package_for_file (PkPostTrans *post, const gchar *filename)
 {
 	guint size;
-	gchar *name = NULL;
-	const PkPackageObj *obj;
+	const PkPackageObj *obj = NULL;
 	PkStore *store;
 
 	/* use PK to find the correct package */
@@ -136,12 +135,8 @@ pk_post_trans_import_desktop_files_get_package (PkPostTrans *post, const gchar *
 		egg_warning ("cannot get obj");
 		goto out;
 	}
-
-	/* strip the name */
-	name = g_strdup (obj->id->name);
-
 out:
-	return name;
+	return obj;
 }
 
 /**
@@ -271,6 +266,7 @@ pk_post_trans_sqlite_add_filename (PkPostTrans *post, const gchar *filename, con
 	gchar *md5 = NULL;
 	gchar *package = NULL;
 	gint rc = -1;
+	const PkPackageObj *obj;
 
 	/* if we've got it, use old data */
 	if (md5_opt != NULL)
@@ -279,14 +275,14 @@ pk_post_trans_sqlite_add_filename (PkPostTrans *post, const gchar *filename, con
 		md5 = pk_post_trans_get_filename_md5 (filename);
 
 	/* resolve */
-	package = pk_post_trans_import_desktop_files_get_package (post, filename);
-	if (package == NULL) {
+	obj = pk_post_trans_get_installed_package_for_file (post, filename);
+	if (obj == NULL) {
 		egg_warning ("failed to get list");
 		goto out;
 	}
 
 	/* add */
-	rc = pk_post_trans_sqlite_add_filename_details (post, filename, package, md5);
+	rc = pk_post_trans_sqlite_add_filename_details (post, filename, obj->id->name, md5);
 out:
 	g_free (md5);
 	g_free (package);
commit f904fd4fe9fe6e4b1d5cffe643ee1ec023d89be0
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 15:11:43 2009 +0100

    Add a thin cached wrapper around lsof so we can track what applications are using open libraries

diff --git a/src/Makefile.am b/src/Makefile.am
index 30ad4e5..6ada872 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -51,6 +51,8 @@ shared_SOURCES =					\
 	egg-dbus-monitor.h				\
 	pk-marshal.c					\
 	pk-marshal.h					\
+	pk-lsof.c					\
+	pk-lsof.h					\
 	pk-transaction.c				\
 	pk-transaction.h				\
 	pk-backend.c					\
diff --git a/src/pk-lsof.c b/src/pk-lsof.c
new file mode 100644
index 0000000..5130728
--- /dev/null
+++ b/src/pk-lsof.c
@@ -0,0 +1,294 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Richard Hughes <richard at hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <glib.h>
+
+#include "egg-debug.h"
+
+#include "pk-lsof.h"
+
+#define PK_LSOF_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), PK_TYPE_LSOF, PkLsofPrivate))
+
+struct PkLsofPrivate
+{
+	GPtrArray		*list_data;
+};
+
+G_DEFINE_TYPE (PkLsof, pk_lsof, G_TYPE_OBJECT)
+
+typedef struct {
+	guint			 pid;
+	gchar			*filename;
+} PkLsofData;
+
+/**
+ * pk_lsof_add_pid:
+ **/
+static gboolean
+pk_lsof_add_pid (GPtrArray *array, guint pid)
+{
+	guint i;
+	guint pid_tmp;
+	gboolean found = FALSE;
+
+	/* search already list */
+	for (i=0; i<array->len; i++) {
+		pid_tmp = GPOINTER_TO_INT (g_ptr_array_index (array, i));
+		if (pid_tmp == pid) {
+			found = TRUE;
+			break;
+		}
+	}
+
+	/* not found, so add */
+	if (!found) {
+		g_ptr_array_add (array, GINT_TO_POINTER (pid));
+	}
+	return !found;
+}
+
+/**
+ * pk_lsof_data_free:
+ **/
+static void
+pk_lsof_data_free (PkLsofData *lsof)
+{
+	g_free (lsof->filename);
+	g_free (lsof);
+}
+
+/**
+ * pk_lsof_data_new:
+ **/
+static PkLsofData *
+pk_lsof_data_new (guint pid, const gchar *filename)
+{
+	PkLsofData *data;
+	data = g_new0 (PkLsofData, 1);
+	data->pid = pid;
+	data->filename = g_strdup (filename);
+	return data;
+}
+
+/**
+ * pk_lsof_refresh:
+ **/
+gboolean
+pk_lsof_refresh (PkLsof *lsof)
+{
+	gboolean ret;
+	GError *error = NULL;
+	gchar *stdout = NULL;
+	gchar *stderr = NULL;
+	PkLsofData *data;
+	gchar **lines = NULL;
+	guint i;
+	const gchar *pid_text;
+	const gchar *type;
+	const gchar *filename;
+
+	g_return_val_if_fail (PK_IS_LSOF (lsof), FALSE);
+
+	/* run lsof to get all data */
+	ret = g_spawn_command_line_sync ("/usr/sbin/lsof", &stdout, &stderr, NULL, &error);
+	if (!ret) {
+		egg_warning ("failed to get pids: %s", error->message);
+		g_error_free (error);
+		goto out;
+	}
+
+	/* clear */
+	g_ptr_array_foreach (lsof->priv->list_data, (GFunc) pk_lsof_data_free, NULL);
+	g_ptr_array_set_size (lsof->priv->list_data, 0);
+
+	/* split into lines */
+	lines = g_strsplit (stdout, "\n", -1);
+	for (i=0; lines[i] != NULL; i++) {
+
+		/* parse: devhelp   11328   hughsie  mem       REG        8,2    65840  161702 /usr/lib/gio/modules/libgioremote-volume-monitor.so */
+		lines[i][17] = '\0';
+		lines[i][36] = '\0';
+		pid_text = &lines[i][10];
+		type = &lines[i][27];
+		filename = &lines[i][69+3];
+
+		/* only add memory mapped entries */
+		if (!g_str_has_prefix (type, "mem"))
+			continue;
+
+		/* not a system library */
+		if (strstr (filename, "/lib/") == NULL)
+			continue;
+
+		/* not a shared object */
+		if (strstr (filename, ".so") == NULL)
+			continue;
+
+		/* add to array */
+		data = pk_lsof_data_new (atoi (pid_text), filename);
+		g_ptr_array_add (lsof->priv->list_data, data);
+	}
+out:
+	g_strfreev (lines);
+	g_free (stdout);
+	g_free (stderr);
+	return ret;
+}
+
+/**
+ * pk_lsof_get_pids_for_filenames:
+ **/
+GPtrArray *
+pk_lsof_get_pids_for_filenames (PkLsof *lsof, gchar **filenames)
+{
+	guint i;
+	guint j;
+	gboolean ret;
+	GPtrArray *list_data;
+	GPtrArray *pids = NULL;
+	const PkLsofData *data;
+
+	g_return_val_if_fail (PK_IS_LSOF (lsof), NULL);
+
+	/* might not have been refreshed ever */
+	list_data = lsof->priv->list_data;
+	if (list_data->len == 0) {
+		ret = pk_lsof_refresh (lsof);
+		if (!ret) {
+			egg_warning ("failed to refresh");
+			goto out;
+		}
+	}
+
+	/* create array of pids that are using this library */
+	pids = g_ptr_array_new ();
+	for (i=0; filenames[i] != NULL; i++) {
+		for (j=0; j < list_data->len; j++) {
+			data = g_ptr_array_index (list_data, j);
+			if (g_strcmp0 (filenames[i], data->filename) == 0) {
+				pk_lsof_add_pid (pids, data->pid);
+			}
+		}
+	}
+out:
+	return pids;
+}
+
+/**
+ * pk_lsof_finalize:
+ **/
+static void
+pk_lsof_finalize (GObject *object)
+{
+	PkLsof *lsof;
+
+	g_return_if_fail (object != NULL);
+	g_return_if_fail (PK_IS_LSOF (object));
+	lsof = PK_LSOF (object);
+
+	g_ptr_array_foreach (lsof->priv->list_data, (GFunc) pk_lsof_data_free, NULL);
+	g_ptr_array_free (lsof->priv->list_data, TRUE);
+
+	G_OBJECT_CLASS (pk_lsof_parent_class)->finalize (object);
+}
+
+/**
+ * pk_lsof_class_init:
+ **/
+static void
+pk_lsof_class_init (PkLsofClass *klass)
+{
+	GObjectClass *object_class = G_OBJECT_CLASS (klass);
+	object_class->finalize = pk_lsof_finalize;
+	g_type_class_add_private (klass, sizeof (PkLsofPrivate));
+}
+
+/**
+ * pk_lsof_init:
+ *
+ * initializes the lsof class. NOTE: We expect lsof objects
+ * to *NOT* be removed or added during the session.
+ * We only control the first lsof object if there are more than one.
+ **/
+static void
+pk_lsof_init (PkLsof *lsof)
+{
+	lsof->priv = PK_LSOF_GET_PRIVATE (lsof);
+	lsof->priv->list_data = g_ptr_array_new ();
+}
+
+/**
+ * pk_lsof_new:
+ * Return value: A new lsof class instance.
+ **/
+PkLsof *
+pk_lsof_new (void)
+{
+	PkLsof *lsof;
+	lsof = g_object_new (PK_TYPE_LSOF, NULL);
+	return PK_LSOF (lsof);
+}
+
+/***************************************************************************
+ ***                          MAKE CHECK TESTS                           ***
+ ***************************************************************************/
+#ifdef EGG_TEST
+#include "egg-test.h"
+
+void
+pk_lsof_test (EggTest *test)
+{
+	gboolean ret;
+	PkLsof *lsof;
+	GPtrArray *pids;
+	gchar *files[] = { "/lib/libssl3.so", NULL };
+
+	if (!egg_test_start (test, "PkLsof"))
+		return;
+
+	/************************************************************/
+	egg_test_title (test, "get an instance");
+	lsof = pk_lsof_new ();
+	egg_test_assert (test, lsof != NULL);
+
+	/************************************************************/
+	egg_test_title (test, "refresh lsof data");
+	ret = pk_lsof_refresh (lsof);
+	egg_test_assert (test, ret);
+
+	/************************************************************/
+	egg_test_title (test, "get pids for files");
+	pids = pk_lsof_get_pids_for_filenames (lsof, files);
+	egg_test_assert (test, pids->len > 0);
+	g_ptr_array_free (pids, TRUE);
+
+	g_object_unref (lsof);
+
+	egg_test_end (test);
+}
+#endif
+
diff --git a/src/pk-lsof.h b/src/pk-lsof.h
new file mode 100644
index 0000000..d6de46f
--- /dev/null
+++ b/src/pk-lsof.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2009 Richard Hughes <richard at hughsie.com>
+ *
+ * Licensed under the GNU General Public License Version 2
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __PK_LSOF_H
+#define __PK_LSOF_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define PK_TYPE_LSOF		(pk_lsof_get_type ())
+#define PK_LSOF(o)		(G_TYPE_CHECK_INSTANCE_CAST ((o), PK_TYPE_LSOF, PkLsof))
+#define PK_LSOF_CLASS(k)	(G_TYPE_CHECK_CLASS_CAST((k), PK_TYPE_LSOF, PkLsofClass))
+#define PK_IS_LSOF(o)		(G_TYPE_CHECK_INSTANCE_TYPE ((o), PK_TYPE_LSOF))
+#define PK_IS_LSOF_CLASS(k)	(G_TYPE_CHECK_CLASS_TYPE ((k), PK_TYPE_LSOF))
+#define PK_LSOF_GET_CLASS(o)	(G_TYPE_INSTANCE_GET_CLASS ((o), PK_TYPE_LSOF, PkLsofClass))
+
+typedef struct PkLsofPrivate PkLsofPrivate;
+
+typedef struct
+{
+	GObject		 parent;
+	PkLsofPrivate	*priv;
+} PkLsof;
+
+typedef struct
+{
+	GObjectClass	parent_class;
+} PkLsofClass;
+
+GType		 pk_lsof_get_type		(void);
+PkLsof		*pk_lsof_new			(void);
+
+gboolean	 pk_lsof_refresh		(PkLsof		*lsof);
+GPtrArray	*pk_lsof_get_pids_for_filenames	(PkLsof		*lsof,
+						 gchar		**filenames);
+
+G_END_DECLS
+
+#endif /* __PK_LSOF_H */
+
diff --git a/src/pk-self-test.c b/src/pk-self-test.c
index ccaf919..753541a 100644
--- a/src/pk-self-test.c
+++ b/src/pk-self-test.c
@@ -26,6 +26,7 @@
 
 /* prototypes */
 void egg_string_test (EggTest *test);
+void pk_lsof_test (EggTest *test);
 void pk_conf_test (EggTest *test);
 void pk_store_test (EggTest *test);
 void pk_inhibit_test (EggTest *test);
@@ -53,6 +54,7 @@ main (int argc, char **argv)
 	egg_string_test (test);
 
 	/* components */
+	pk_lsof_test (test);
 	pk_file_monitor_test (test);
 	pk_time_test (test);
 	pk_conf_test (test);
commit 76b59120698415cfbd399ad2860290d4a4239627
Author: Richard Hughes <richard at hughsie.com>
Date:   Sat Jul 11 15:09:32 2009 +0100

    dummy: add to the file list for the new lsof functionality testing

diff --git a/backends/dummy/pk-backend-dummy.c b/backends/dummy/pk-backend-dummy.c
index bbc46db..aad1fe6 100644
--- a/backends/dummy/pk-backend-dummy.c
+++ b/backends/dummy/pk-backend-dummy.c
@@ -243,7 +243,7 @@ backend_get_files (PkBackend *backend, gchar **package_ids)
 		else if (egg_strequal (package_id, "kernel;2.6.23-0.115.rc3.git1.fc8;i386;installed"))
 			pk_backend_files (backend, package_id, "/usr/share/man/man1;/usr/share/man/man1/gnome-power-manager.1.gz");
 		else if (egg_strequal (package_id, "gtkhtml2;2.19.1-4.fc8;i386;fedora"))
-			pk_backend_files (backend, package_id, "/usr/share/man/man1;/usr/bin/ck-xinit-session");
+			pk_backend_files (backend, package_id, "/usr/share/man/man1;/usr/bin/ck-xinit-session;/lib/libselinux.so.1");
 		else
 			pk_backend_files (backend, package_id, "/usr/share/gnome-power-manager;/usr/bin/ck-xinit-session");
 	}


More information about the PackageKit-commit mailing list