hal: Branch 'master' - 2 commits

David Zeuthen david at kemper.freedesktop.org
Wed Jan 10 21:49:11 PST 2007


 configure.in          |    2 
 hald/Makefile.am      |   24 +
 hald/create_cache.c   |  666 +++++++++++++++++++++++++++++++++++++++++++++++++
 hald/debug-hald.sh    |    1 
 hald/device_info.c    |  678 +++++---------------------------------------------
 hald/device_info.h    |    1 
 hald/hald.c           |   12 
 hald/hald_runner.c    |   18 -
 hald/mmap_cache.c     |  239 +++++++++++++++++
 hald/mmap_cache.h     |   37 ++
 hald/rule.h           |  111 ++++++++
 hald/run-hald.sh      |    1 
 hald/valgrind-hald.sh |    1 
 13 files changed, 1176 insertions(+), 615 deletions(-)

New commits:
diff-tree de7ada8fd2a5675554d6723b9145afa4e719e8a0 (from 285b20f453bd4ce5f963a9818711c7ffd38437d7)
Author: David Zeuthen <davidz at redhat.com>
Date:   Thu Jan 11 00:48:57 2007 -0500

    fix up a few things with the new fdi cache
    
     - CACHE_FILE and HALD_CACHE_BIN shouldn't be set from configure.in
    
     - instead of installing the helper in /sbin and caling it hald-cache
       rather call it hald-generate-fdi-cache and install it in /usr/libexec
       since only hald will call it
    
     - make the runner capable of invoking programs when device==NULL
    
     - make hald invoke the fdi cache generator using the runner; otherwise
       ./run-hald.sh and friends will not work if your home directory is
       unreadable by the haldaemon user
    
     - create /var/cache/hald in install-data-local. Also fixup the missing
       use of $DESTDIR of /var/run/hald stuff
    
     - s/cache_coherency_check()/di_cache_coherency_check()/
    
     - wait for runner to be initialized before invoking di_cache_coherency_check()
    
     - fix di_cache_coherency_check() so it respects $HAL_FDI_SOURCE_* and
       also $HAL_FDI_CACHE_NAME
    
     - remote the cache-test program for now as we now require the runner
    
     - fix up ./run-hald.sh and friends to use a cache in .local-fdi thereby
       not polluting the global cache

diff --git a/configure.in b/configure.in
index f7571f0..c479de3 100644
--- a/configure.in
+++ b/configure.in
@@ -450,12 +450,6 @@ fi
 AC_SUBST(DBUS_SYS_DIR)
 AC_DEFINE_UNQUOTED(DBUS_SYSTEMD_DIR, "$DBUS_SYS_DIR", [Where system.d dir for DBUS is])
 
-AC_SUBST(CACHE_FILE)
-AC_DEFINE_UNQUOTED(CACHE_FILE,"$LOCALSTATEDIR/cache/hald/fdi_cache", [Where FDI cache file is stored])
-
-AC_SUBST(HALD_CACHE_BIN)
-AC_DEFINE_UNQUOTED(HALD_CACHE_BIN,"$SBINDIR/hald-cache", [Cache generator binary])
-
 #### Check our operating system (distro-tweaks required)
 operating_system=unknown
 if test -f /etc/redhat-release || test -f SYSCONFDIR/redhat-release ; then
diff --git a/hald/Makefile.am b/hald/Makefile.am
index e72d55d..4023762 100644
--- a/hald/Makefile.am
+++ b/hald/Makefile.am
@@ -30,19 +30,15 @@ INCLUDES = \
 # TESTS = hald-test
 
 
-sbin_PROGRAMS = hald hald-cache
+sbin_PROGRAMS = hald
+libexec_PROGRAMS = hald-generate-fdi-cache
 
 BUILT_SOURCES =		\
 	hald_marshal.h	\
 	hald_marshal.c
 
-noinst_PROGRAMS = hald-cache-test
-
-hald_cache_SOURCES = create_cache.c logger.h logger.c rule.h
-hald_cache_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
-
-hald_cache_test_SOURCES = cache_test.c logger.h logger.c rule.h mmap_cache.c mmap_cache.h
-hald_cache_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
+hald_generate_fdi_cache_SOURCES = create_cache.c logger.h logger.c rule.h
+hald_generate_fdi_cache_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
 
 hald_SOURCES =                                                          \
 	hald_marshal.h			hald_marshal.c			\
@@ -91,11 +87,13 @@ hald_marshal.c: hald_marshal.list
 
 
 install-data-local:
-	-$(mkdir_p) $(HALD_SOCKET_DIR)
-	-chown $(HAL_USER):$(HAL_GROUP) $(HALD_SOCKET_DIR)
-	-chmod 0755 $(HALD_SOCKET_DIR)
-	-$(mkdir_p) $(HALD_SOCKET_DIR)/hald-local
-	-$(mkdir_p) $(HALD_SOCKET_DIR)/hald-runner
+	-$(mkdir_p) $(DESTDIR)$(HALD_SOCKET_DIR)
+	-chown $(HAL_USER):$(HAL_GROUP) $(DESTDIR)$(HALD_SOCKET_DIR)
+	-chmod 0755 $(DESTDIR)$(HALD_SOCKET_DIR)
+	-$(mkdir_p) $(DESTDIR)$(HALD_SOCKET_DIR)/hald-local
+	-$(mkdir_p) $(DESTDIR)$(HALD_SOCKET_DIR)/hald-runner
+	-$(mkdir_p) $(DESTDIR)$(localstatedir)/cache/hald
+	-chown $(HAL_USER):$(HAL_GROUP) $(DESTDIR)$(localstatedir)/cache/hald
 
 clean-local:
 	rm -f *~
diff --git a/hald/cache_test.c b/hald/cache_test.c
deleted file mode 100644
index a5a499d..0000000
--- a/hald/cache_test.c
+++ /dev/null
@@ -1,79 +0,0 @@
-/***************************************************************************
- * CVSID: $Id$
- *
- * cache_test.c : test FDI cache.
- *
- * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
- * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
- * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
- * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
- * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
- *
- * Licensed under the Academic Free License version 2.1
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
- *
- **************************************************************************/
-
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/mman.h>
-#include <fcntl.h>
-#include <unistd.h>
-#include <string.h>
-#include <stdio.h>
-#include <malloc.h>
-#include <dirent.h>
-#include <errno.h>
-#include <expat.h>
-#include <glib.h>
-#include <config.h>
-
-#include "logger.h"
-#include "rule.h"
-#include "mmap_cache.h"
-
-void 	*rules_ptr = NULL;
-
-static void test_cache(u_int32_t offset, size_t size){
-    u_int32_t		m = offset;
-    struct rule 	*r;
-
-    while(m < offset + size){
-	r = (struct rule *) RULES_PTR(m);
-
-	HAL_INFO(("rule=%08lx, rule_size=%d, rtype=%d", m, r->rule_size, r->rtype));
-	HAL_INFO(("  jump_position=%08lx", r->jump_position));
-	HAL_INFO(("  key='%s', key_len=%d, key_offset=%08lx",
-		r->key, r->key_len, m + offsetof(struct rule, key)));
-	HAL_INFO(("  value='%s', value_len=%d, value_offset=%08lx",
-		(char*)rules_ptr + r->value_offset, r->value_len, r->value_offset));
-
-	m += r->rule_size;
-    }
-}
-
-int main(){
-    struct cache_header	*header;
-
-    cache_coherency_check();
-    di_rules_init();
-    header = (struct cache_header*) RULES_PTR(0);
-
-    test_cache(header->fdi_rules_preprobe, header->fdi_rules_information - header->fdi_rules_preprobe);
-    test_cache(header->fdi_rules_information, header->fdi_rules_policy - header->fdi_rules_information);
-    test_cache(header->fdi_rules_policy, header->all_rules_size - header->fdi_rules_policy);
-    return 0;
-}
diff --git a/hald/create_cache.c b/hald/create_cache.c
index e4c2c30..4d016d1 100644
--- a/hald/create_cache.c
+++ b/hald/create_cache.c
@@ -513,7 +513,8 @@ di_rules_init (void)
 	char *hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
 
 	cachename = getenv ("HAL_FDI_CACHE_NAME");
-	if(!cachename) cachename = CACHE_FILE;
+	if(cachename == NULL)
+		cachename = HALD_CACHE_FILE;
 	HAL_INFO (("Loading rules"));
 	
 	if(haldc_force_recreate && !stat(cachename, &st)) {
diff --git a/hald/debug-hald.sh b/hald/debug-hald.sh
index bda8c94..379ff49 100755
--- a/hald/debug-hald.sh
+++ b/hald/debug-hald.sh
@@ -17,6 +17,7 @@ fi
 export HAL_FDI_SOURCE_PREPROBE=.local-fdi/share/hal/fdi/preprobe
 export HAL_FDI_SOURCE_INFORMATION=.local-fdi/share/hal/fdi/information
 export HAL_FDI_SOURCE_POLICY=.local-fdi/share/hal/fdi/policy
+export HAL_FDI_CACHE_NAME=.local-fdi/hald-local-fdi-cache
 
 echo ========================================
 echo Just type \'run\' to start debugging hald
diff --git a/hald/hald.c b/hald/hald.c
index 8c7875b..4e4f1c6 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -505,8 +505,6 @@ main (int argc, char *argv[])
 
 	HAL_INFO ((PACKAGE_STRING));
 	
-	cache_coherency_check();
-
 	if (opt_become_daemon) {
 		int child_pid;
 		int dev_null_fd;
@@ -613,11 +611,14 @@ main (int argc, char *argv[])
 		drop_privileges(0);
 	}
 
+	hald_is_initialising = TRUE;
+
+	/* make sure our fdi rule cache is up to date */
+	di_cache_coherency_check();
+
 	/* initialize operating system specific parts */
 	osspec_init ();
 
-	hald_is_initialising = TRUE;
-
 	/* Init FDI files */
 	di_rules_init();
 
diff --git a/hald/hald_runner.c b/hald/hald_runner.c
index 32abd9c..41fa987 100644
--- a/hald/hald_runner.c
+++ b/hald/hald_runner.c
@@ -397,14 +397,19 @@ add_first_part(DBusMessageIter *iter, Ha
   DBusMessageIter array_iter;
   const char *udi;
 
-  udi = hal_device_get_udi(device);
+  if (device != NULL)
+	  udi = hal_device_get_udi(device);
+  else
+	  udi = "";
+
   dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &udi);
 
   dbus_message_iter_open_container(iter, 
                                    DBUS_TYPE_ARRAY,
                                    DBUS_TYPE_STRING_AS_STRING,
                                    &array_iter);
-  hal_device_property_foreach (device, add_property_to_msg, &array_iter);
+  if (device != NULL)
+	  hal_device_property_foreach (device, add_property_to_msg, &array_iter);
   add_basic_env(&array_iter, udi);
   add_extra_env(&array_iter, extra_env);
   dbus_message_iter_close_container(iter, &array_iter);
@@ -512,7 +517,8 @@ call_notify(DBusPendingCall *pending, vo
   hb->cb(hb->d, exitt, return_code, 
       (gchar **)error->data, hb->data1, hb->data2);
 
-  g_object_unref (hb->d);
+  if (hb->d != NULL)
+	  g_object_unref (hb->d);
 
   dbus_message_unref(m);
   g_array_free(error, TRUE);
@@ -526,7 +532,8 @@ malformed:
   HAL_ERROR (("Malformed or unexpected reply message"));
   hb->cb(hb->d, HALD_RUN_FAILED, return_code, NULL, hb->data1, hb->data2);
 
-  g_object_unref (hb->d);
+  if (hb->d != NULL)
+	  g_object_unref (hb->d);
 
   dbus_message_unref(m);
   g_array_free(error, TRUE);
@@ -575,7 +582,8 @@ hald_runner_run_method(HalDevice *device
   hd->data1 = data1;
   hd->data2 = data2;
 
-  g_object_ref (device);
+  if (device != NULL)
+	  g_object_ref (device);
 
   dbus_pending_call_set_notify(call, call_notify, hd, NULL);
   dbus_message_unref(msg);
diff --git a/hald/mmap_cache.c b/hald/mmap_cache.c
index e43c6da..45e0ebe 100644
--- a/hald/mmap_cache.c
+++ b/hald/mmap_cache.c
@@ -43,6 +43,7 @@
 #include "logger.h"
 #include "rule.h"
 #include "mmap_cache.h"
+#include "hald_runner.h"
 
 extern void *rules_ptr;
 
@@ -53,7 +54,8 @@ void di_rules_init(void){
 	struct stat		statbuf;
 
 	cachename = getenv ("HAL_FDI_CACHE_NAME");
-	if(!cachename) cachename = CACHE_FILE;
+	if(cachename == NULL) 
+		cachename = HALD_CACHE_FILE;
 
 	if((fd = open(cachename, O_RDONLY)) < 0)
 		DIE(("Unable to open cache %s\n", cachename));
@@ -78,83 +80,160 @@ void di_rules_init(void){
 
 static void dir_mtime(const char * path, time_t * mt)
 {
-    struct dirent **namelist;
-    struct stat st;
-    int n;
-    char cpath[PATH_MAX];
-
-    if (!stat(path, &st)) {
-	if(st.st_mtime > *mt)
-	    *mt = st.st_mtime;
-
-	if(S_ISDIR(st.st_mode)) {
-    		n = scandir(path, &namelist, 0, alphasort);
-    		if (n < 0)
-        		return;
-    		else {
-            		while(n--) {
+	struct dirent **namelist;
+	struct stat st;
+	int n;
+	char cpath[PATH_MAX];
+	
+	if (!stat(path, &st)) {
+		if(st.st_mtime > *mt)
+			*mt = st.st_mtime;
+		
+		if(S_ISDIR(st.st_mode)) {
+			n = scandir(path, &namelist, 0, alphasort);
+			if (n < 0)
+				return;
+			else {
+				while(n--) {
 #ifdef HAVE_SNPRINTF
-			    snprintf(cpath, PATH_MAX, "%s/%s", path, namelist[n]->d_name);
+					snprintf(cpath, PATH_MAX, "%s/%s", path, namelist[n]->d_name);
 #else
-			    sprintf(cpath, "%s/%s", path, namelist[n]->d_name);
+					sprintf(cpath, "%s/%s", path, namelist[n]->d_name);
 #endif
-			    if(namelist[n]->d_name[0] != '.')
-				    dir_mtime(cpath, mt);
+					if(namelist[n]->d_name[0] != '.')
+						dir_mtime(cpath, mt);
+					
+					free(namelist[n]);
+				}
+				free(namelist);
+			}
+		}
+	}
+}
+
+
+static gboolean regen_cache_done;
+static gint regen_cache_success;
 
-                	    free(namelist[n]);
-            	    }
-            	    free(namelist);
-    		}
+static void 
+regen_cache_cb (HalDevice *d, 
+		guint32 exit_type, 
+		gint return_code, 
+		gchar **error,
+		gpointer data1, 
+		gpointer data2)
+{
+	HAL_INFO (("In regen_cache_cb exit_type=%d, return_code=%d", exit_type, return_code));
+
+	if (exit_type != HALD_RUN_SUCCESS || return_code != 0) {
+		regen_cache_success = FALSE;
+	} else {
+		regen_cache_success = TRUE;
 	}
-    }
+
+	regen_cache_done = TRUE;
 }
 
-static void regen_cache(void)
+
+static void 
+regen_cache (void)
 {
-    int ret;
-    struct stat st;
-    char cmd [PATH_MAX + 9];
-    char * c;
-    
-    if((c = getenv("HALD_CACHE_BIN")) == NULL) {
-	c = HALD_CACHE_BIN;
-    }
-#ifdef HAVE_SNPRINTF
-    snprintf(cmd, PATH_MAX + 8, "%s --force", c);
-#else
-    sprintf(cmd, "%s --force", c);
-#endif
+	int n, m;
+	char *extra_env[5] = {NULL, NULL, NULL, NULL, NULL};
+	char *env_names[5] = {"HAL_FDI_SOURCE_PREPROBE", 
+			      "HAL_FDI_SOURCE_INFORMATION", 
+			      "HAL_FDI_SOURCE_POLICY",
+			      "HAL_FDI_CACHE_NAME",
+			      NULL};
+
+	HAL_INFO (("Regenerating fdi cache.."));
+
+	/* pass these variables to the helper */
+	for (n = 0, m = 0; env_names[n] != NULL; n++) {
+		char *name;
+		char *val;
+
+		name = env_names[n];
+		val = getenv(name);
+		if (val != NULL) {
+			extra_env [m++] = g_strdup_printf ("%s=%s", name, val);
+		}
+	}
+
+	regen_cache_done = FALSE;
+
+	hald_runner_run (NULL, 
+			 "hald-generate-fdi-cache --force",
+			 extra_env,
+			 10000,
+			 regen_cache_cb,
+			 NULL,
+			 NULL);
 
-    if(!stat(c, &st) && (ret = system(cmd)) !=- 1) {
+	for (n = 0; extra_env[n] != NULL; n++) {
+		g_free (extra_env[n]);
+	}
+
+	while (!regen_cache_done) {
+		g_main_context_iteration (NULL, TRUE);
+	}
 
-	if(WTERMSIG(ret) == SIGQUIT || WTERMSIG(ret) == SIGINT)
-		DIE(("Cache creation was interrupted"));
+	if (!regen_cache_success) {
+		DIE (("fdi cache regeneration failed!"));
+	}
 
-	if(WEXITSTATUS(ret) == 0)
-		HAL_INFO(("Subprocess terminated normally, cache generated."));
-    }
-    else {
-	HAL_INFO(("Unable to generate cache"));
-    }
+	HAL_INFO (("fdi cache generation done"));
 }
-void cache_coherency_check(void)
+
+void 
+di_cache_coherency_check (void)
 {
- time_t mt;
- struct stat st;
- dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/preprobe", &mt);
- dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe", &mt);
- dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/information", &mt);
- dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/information", &mt);
- dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/policy", &mt);
- dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/policy", &mt);
- if(!stat(CACHE_FILE, &st)) {
-	if(st.st_mtime < mt) {
-		HAL_INFO(("Cache needs update"));
-		regen_cache();
+	char *hal_fdi_source_preprobe;
+	char *hal_fdi_source_information;
+	char *hal_fdi_source_policy;
+	char *cachename;
+	time_t mt;
+	struct stat st;
+
+	mt = 0;
+
+	hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
+	hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
+	hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
+
+	if (hal_fdi_source_preprobe != NULL) {
+		dir_mtime (hal_fdi_source_preprobe, &mt);
+	} else {
+		dir_mtime (PACKAGE_DATA_DIR "/hal/fdi/preprobe", &mt);
+		dir_mtime (PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe", &mt);
+	}
+
+	if (hal_fdi_source_information != NULL) {
+		dir_mtime (hal_fdi_source_information, &mt);
+	} else {
+		dir_mtime (PACKAGE_DATA_DIR "/hal/fdi/information", &mt);
+		dir_mtime (PACKAGE_SYSCONF_DIR "/hal/fdi/information", &mt);
+	}
+
+	if (hal_fdi_source_policy != NULL) {
+		dir_mtime (hal_fdi_source_policy, &mt);
+	} else {
+		dir_mtime (PACKAGE_DATA_DIR "/hal/fdi/policy", &mt);
+		dir_mtime (PACKAGE_SYSCONF_DIR "/hal/fdi/policy", &mt);
 	}
- }
- else
-	regen_cache();
 
- HAL_INFO(("cache mtime is %d",mt));
+	cachename = getenv ("HAL_FDI_CACHE_NAME");
+	if(cachename == NULL)
+		cachename = HALD_CACHE_FILE;
+
+	if(stat (cachename, &st) == 0) {
+		if(st.st_mtime < mt) {
+			HAL_INFO(("Cache needs update"));
+			regen_cache();
+		}
+	} else {
+		regen_cache();
+	}
+	
+	HAL_INFO(("cache mtime is %d",mt));
 }
diff --git a/hald/mmap_cache.h b/hald/mmap_cache.h
index 5c136d5..bfafeb4 100644
--- a/hald/mmap_cache.h
+++ b/hald/mmap_cache.h
@@ -31,7 +31,7 @@
 #ifndef __MMAP_CACHE_H__
 
 void di_rules_init(void);
-void cache_coherency_check(void);
+void di_cache_coherency_check(void);
 
 #define RULES_PTR(x) ((void *)((unsigned char *) rules_ptr + x))
 #endif
diff --git a/hald/rule.h b/hald/rule.h
index 9c726d3..cb82e62 100644
--- a/hald/rule.h
+++ b/hald/rule.h
@@ -106,4 +106,6 @@ struct cache_header {
 
 #define HAL_MAX_INDENT_DEPTH		64
 
+#define HALD_CACHE_FILE PACKAGE_LOCALSTATEDIR "/cache/hald/fdi-cache"
+
 #endif
diff --git a/hald/run-hald.sh b/hald/run-hald.sh
index ed32d1e..88d55e1 100755
--- a/hald/run-hald.sh
+++ b/hald/run-hald.sh
@@ -25,6 +25,7 @@ fi
 export HAL_FDI_SOURCE_PREPROBE=.local-fdi/share/hal/fdi/preprobe
 export HAL_FDI_SOURCE_INFORMATION=.local-fdi/share/hal/fdi/information
 export HAL_FDI_SOURCE_POLICY=.local-fdi/share/hal/fdi/policy
+export HAL_FDI_CACHE_NAME=.local-fdi/hald-local-fdi-cache
 
 ./hald --daemon=no --verbose=yes $@
 #./hald --daemon=no
diff --git a/hald/valgrind-hald.sh b/hald/valgrind-hald.sh
index eab6e5c..28bd581 100755
--- a/hald/valgrind-hald.sh
+++ b/hald/valgrind-hald.sh
@@ -17,6 +17,7 @@ fi
 export HAL_FDI_SOURCE_PREPROBE=.local-fdi/share/hal/fdi/preprobe
 export HAL_FDI_SOURCE_INFORMATION=.local-fdi/share/hal/fdi/information
 export HAL_FDI_SOURCE_POLICY=.local-fdi/share/hal/fdi/policy
+export HAL_FDI_CACHE_NAME=.local-fdi/hald-local-fdi-cache
 
 #valgrind --num-callers=20 --show-reachable=yes --leak-check=yes --tool=memcheck ./hald --daemon=no --verbose=yes $@
 valgrind --show-reachable=yes --tool=memcheck --leak-check=full ./hald --daemon=no --verbose=yes $@
diff-tree 285b20f453bd4ce5f963a9818711c7ffd38437d7 (from cdca0a7dc663d5e174e811b2c17afd7491537aa2)
Author: Sergey Lapin <slapinid at gmail.com>
Date:   Wed Jan 10 22:55:09 2007 -0500

    move fdi file parsing into a separate process
    
    David Zeuthen wrote:
    > On Tue, 2007-01-09 at 16:20 +0300, Sergey Lapin wrote:
    >> Hi!
    >>
    >> Attached is a patch which provides these improvements:
    >>
    >> - Separates XML parsing from main running process thus reducing memory
    >> fragmentation and consumption.
    >> - maintains binary cache which is mmapped for reading thus not using
    >> heap for that, so it uses mapped memory which is freed on demand which
    >> is good for small-memory embedded devices.
    >> - makes hald smaller due to fact that bunch of rarely used code is out
    >> in separate process.
    
    It is updated version of patch:
    
    - Fixed code style issues and cut-paste comment errors
    - Created trivial cache coherency check (mtime)
    - moved test binary to noinst
    - made proper command line options for hald-cache
    - proper logger setup with hald-cache

diff --git a/configure.in b/configure.in
index 54639de..f7571f0 100644
--- a/configure.in
+++ b/configure.in
@@ -388,6 +388,7 @@ fi
 AC_CHECK_FUNCS(getgrouplist)
 AC_CHECK_FUNCS(asprintf)
 AC_CHECK_FUNCS(mallopt)
+AC_CHECK_FUNCS(strndup)
 
 # DocBook Documentation
 
@@ -449,6 +450,11 @@ fi
 AC_SUBST(DBUS_SYS_DIR)
 AC_DEFINE_UNQUOTED(DBUS_SYSTEMD_DIR, "$DBUS_SYS_DIR", [Where system.d dir for DBUS is])
 
+AC_SUBST(CACHE_FILE)
+AC_DEFINE_UNQUOTED(CACHE_FILE,"$LOCALSTATEDIR/cache/hald/fdi_cache", [Where FDI cache file is stored])
+
+AC_SUBST(HALD_CACHE_BIN)
+AC_DEFINE_UNQUOTED(HALD_CACHE_BIN,"$SBINDIR/hald-cache", [Cache generator binary])
 
 #### Check our operating system (distro-tweaks required)
 operating_system=unknown
diff --git a/hald/Makefile.am b/hald/Makefile.am
index e1daa08..e72d55d 100644
--- a/hald/Makefile.am
+++ b/hald/Makefile.am
@@ -30,12 +30,20 @@ INCLUDES = \
 # TESTS = hald-test
 
 
-sbin_PROGRAMS = hald
+sbin_PROGRAMS = hald hald-cache
 
 BUILT_SOURCES =		\
 	hald_marshal.h	\
 	hald_marshal.c
 
+noinst_PROGRAMS = hald-cache-test
+
+hald_cache_SOURCES = create_cache.c logger.h logger.c rule.h
+hald_cache_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
+
+hald_cache_test_SOURCES = cache_test.c logger.h logger.c rule.h mmap_cache.c mmap_cache.h
+hald_cache_test_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
+
 hald_SOURCES =                                                          \
 	hald_marshal.h			hald_marshal.c			\
 	util.h				util.c				\
@@ -49,9 +57,13 @@ hald_SOURCES =                          
 	hald_dbus.h			hald_dbus.c			\
 	logger.h			logger.c			\
 	osspec.h							\
-	ids.h				ids.c			
+	ids.h				ids.c				\
+	rule.h				mmap_cache.c			\
+	mmap_cache.h
+
+
 
-hald_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @EXPAT_LIB@ @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
+hald_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ -lm @HALD_OS_LIBS@ $(top_builddir)/hald/$(HALD_BACKEND)/libhald_$(HALD_BACKEND).la
 
 #### Init scripts fun
 SCRIPT_IN_FILES=haldaemon.in
diff --git a/hald/cache_test.c b/hald/cache_test.c
new file mode 100644
index 0000000..a5a499d
--- /dev/null
+++ b/hald/cache_test.c
@@ -0,0 +1,79 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * cache_test.c : test FDI cache.
+ *
+ * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
+ * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <malloc.h>
+#include <dirent.h>
+#include <errno.h>
+#include <expat.h>
+#include <glib.h>
+#include <config.h>
+
+#include "logger.h"
+#include "rule.h"
+#include "mmap_cache.h"
+
+void 	*rules_ptr = NULL;
+
+static void test_cache(u_int32_t offset, size_t size){
+    u_int32_t		m = offset;
+    struct rule 	*r;
+
+    while(m < offset + size){
+	r = (struct rule *) RULES_PTR(m);
+
+	HAL_INFO(("rule=%08lx, rule_size=%d, rtype=%d", m, r->rule_size, r->rtype));
+	HAL_INFO(("  jump_position=%08lx", r->jump_position));
+	HAL_INFO(("  key='%s', key_len=%d, key_offset=%08lx",
+		r->key, r->key_len, m + offsetof(struct rule, key)));
+	HAL_INFO(("  value='%s', value_len=%d, value_offset=%08lx",
+		(char*)rules_ptr + r->value_offset, r->value_len, r->value_offset));
+
+	m += r->rule_size;
+    }
+}
+
+int main(){
+    struct cache_header	*header;
+
+    cache_coherency_check();
+    di_rules_init();
+    header = (struct cache_header*) RULES_PTR(0);
+
+    test_cache(header->fdi_rules_preprobe, header->fdi_rules_information - header->fdi_rules_preprobe);
+    test_cache(header->fdi_rules_information, header->fdi_rules_policy - header->fdi_rules_information);
+    test_cache(header->fdi_rules_policy, header->all_rules_size - header->fdi_rules_policy);
+    return 0;
+}
diff --git a/hald/create_cache.c b/hald/create_cache.c
new file mode 100644
index 0000000..e4c2c30
--- /dev/null
+++ b/hald/create_cache.c
@@ -0,0 +1,665 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * create_cache.c : create FDI cache.
+ *
+ * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
+ * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <malloc.h>
+#include <errno.h>
+#include <string.h>
+#include <getopt.h>
+#include <syslog.h>
+#include <expat.h>
+#include <glib.h>
+#include <config.h>
+
+#include "logger.h"
+#include "rule.h"
+
+/* ctx of the current fdi file used for parsing */
+struct fdi_context {
+	int		depth;
+	u_int32_t	match_at_depth[HAL_MAX_INDENT_DEPTH];
+	struct rule	rule;
+	off_t		position;
+	int		cache_fd;
+};
+
+/* modified alphasort to count downwards */
+static int
+#ifdef __GLIBC__
+_alphasort(const void *a, const void *b)
+#else
+_alphasort(const struct dirent **a, const struct dirent **b)
+#endif
+{
+	return -alphasort (a, b);
+}
+
+
+static enum
+rule_type get_rule_type (const char *str)
+{
+	if (strcmp (str, "match") == 0)
+		return RULE_MATCH;
+	if (strcmp (str, "merge") == 0)
+		return RULE_MERGE;
+	if (strcmp (str, "append") == 0)
+		return RULE_APPEND;
+	if (strcmp (str, "prepend") == 0)
+		return RULE_PREPEND;
+	if (strcmp (str, "remove") == 0)
+		return RULE_REMOVE;
+	if (strcmp (str, "clear") == 0)
+		return RULE_CLEAR;
+	if (strcmp (str, "spawn") == 0)
+		return RULE_SPAWN;
+	return RULE_UNKNOWN;
+}
+
+static enum
+match_type get_match_type(const char *str)
+{
+	if (strcmp (str, "string") == 0)
+		return MATCH_STRING;
+	if (strcmp (str, "int") == 0)
+		return MATCH_INT;
+	if (strcmp (str, "uint64") == 0)
+		return MATCH_UINT64;
+	if (strcmp (str, "bool") == 0)
+		return MATCH_BOOL;
+	if (strcmp (str, "exists") == 0)
+		return MATCH_EXISTS;
+	if (strcmp (str, "empty") == 0)
+		return MATCH_EMPTY;
+	if (strcmp (str, "is_ascii") == 0)
+		return MATCH_ISASCII;
+	if (strcmp (str, "is_absolute_path") == 0)
+		return MATCH_IS_ABS_PATH;
+	if (strcmp (str, "contains") == 0)
+		return MATCH_CONTAINS;
+	if (strcmp (str, "contains_ncase") == 0)
+		return MATCH_CONTAINS_NCASE;
+	if (strcmp (str, "prefix") == 0)
+		return MATCH_PREFIX;
+	if (strcmp (str, "prefix_ncase") == 0)
+		return MATCH_PREFIX_NCASE;
+	if (strcmp (str, "suffix") == 0)
+		return MATCH_SUFFIX;
+	if (strcmp (str, "suffix_ncase") == 0)
+		return MATCH_SUFFIX_NCASE;
+	if (strcmp (str, "compare_lt") == 0)
+		return MATCH_COMPARE_LT;
+	if (strcmp (str, "compare_le") == 0)
+		return MATCH_COMPARE_LE;
+	if (strcmp (str, "compare_gt") == 0)
+		return MATCH_COMPARE_GT;
+	if (strcmp (str, "compare_ge") == 0)
+		return MATCH_COMPARE_GE;
+	return MATCH_UNKNOWN;
+}
+
+static enum
+merge_type get_merge_type (const char *str)
+{
+	if (strcmp (str, "string") == 0)
+		return MERGE_STRING;
+	if (strcmp (str, "bool") == 0)
+		return MERGE_BOOLEAN;
+	if (strcmp (str, "int") == 0)
+		return MERGE_INT32;
+	if (strcmp (str, "unint64") == 0)
+		return MERGE_UINT64;
+	if (strcmp (str, "double") == 0)
+		return MERGE_DOUBLE;
+	if (strcmp (str, "strlist") == 0)
+		return MERGE_STRLIST;
+	if (strcmp (str, "copy_property") == 0)
+		return MERGE_COPY_PROPERTY;
+	if (strcmp (str, "remove") == 0)
+		return MERGE_REMOVE;
+	return MERGE_UNKNOWN;
+}
+
+#define ROUND32(len) ((len + 3) & ~0x03)
+
+static void pad32_write(int fd, off_t offset, void *data, size_t len)
+{
+	ssize_t		result;
+	static char	pad[3] = {0, 0, 0};
+
+	lseek(fd, offset, SEEK_SET);
+	result = write(fd, data, len);
+
+	if (result != (ssize_t) len)
+		DIE(("Disk write error"));
+
+	if ((offset + len) & 0x03) {
+		result = write(fd, pad, 4 - ((offset + len) & 0x03));
+
+		if (result != (ssize_t) (4 - ((offset + len) & 0x03)))
+			DIE(("Disk write error"));
+	}
+}
+
+static void init_rule_struct(struct rule *rule)
+{
+	memset(rule, 0, sizeof(struct rule));
+	rule->rule_size = sizeof(struct rule);
+	rule->rtype = RULE_UNKNOWN;
+	rule->type_match = MATCH_UNKNOWN;
+	rule->type_merge = MERGE_UNKNOWN;
+	rule->value_offset = offsetof(struct cache_header, empty_string);
+}
+
+/* stores key string to data file which we'll mmap next */
+static void store_key(struct fdi_context *fdi_ctx, const char *key)
+{
+	if (fdi_ctx->rule.rtype == RULE_UNKNOWN)
+		DIE(("I refuse to store garbage"));
+
+	if ((key == NULL) || (*key == '\0'))
+		DIE(("Key is not valid"));
+
+	fdi_ctx->rule.key_len = strlen(key) + 1;
+
+	pad32_write(fdi_ctx->cache_fd, fdi_ctx->position + sizeof(struct rule),
+	    (void*)key, fdi_ctx->rule.key_len);
+
+	HAL_INFO(("Storing key '%s' at rule=%08lx", key, fdi_ctx->position));
+}
+
+/* stores value string to data file which we'll mmap next */
+static void store_value(struct fdi_context *fdi_ctx, const char *value, size_t value_len)
+{
+	off_t	offset;
+	char * p;
+
+	if (fdi_ctx->rule.rtype == RULE_UNKNOWN)
+		DIE(("I refuse to store garbage"));
+
+	if (fdi_ctx->rule.key_len < 2)
+		DIE(("Key length is too small to be true"));
+
+	if ((value_len == 0) || (value == NULL) || (*value == '\0')) return;
+
+	offset = fdi_ctx->position + sizeof(struct rule) + ROUND32(fdi_ctx->rule.key_len);
+
+	if (fdi_ctx->rule.value_len == 0) {
+		fdi_ctx->rule.value_offset = offset;
+		fdi_ctx->rule.value_len = value_len + 1;
+	}
+	else {
+		offset += fdi_ctx->rule.value_len - 1;
+		fdi_ctx->rule.value_len += value_len;
+	}
+
+	pad32_write(fdi_ctx->cache_fd, offset, (void*)value, value_len);
+	pad32_write(fdi_ctx->cache_fd, offset + value_len, "", 1);
+
+	p = malloc(value_len + 1);
+
+	if(!p)
+	    DIE(("Could not allocate %d bytes", value_len + 1));
+
+	memcpy(p, value, value_len);
+	p[value_len] = '\0';
+	
+	HAL_INFO(("Storing value '%s', value_len=%d, at rule=%08lx, offset=%08lx",
+		p, value_len, fdi_ctx->position, offset));
+
+	free(p);
+}
+
+static void store_rule(struct fdi_context *fdi_ctx)
+{
+	if (fdi_ctx->rule.rtype == RULE_UNKNOWN)
+		DIE(("I refuse to store garbage"));
+	
+	fdi_ctx->rule.rule_size = sizeof(struct rule) +
+		ROUND32(fdi_ctx->rule.key_len) +
+		ROUND32(fdi_ctx->rule.value_len);
+
+	pad32_write(fdi_ctx->cache_fd, fdi_ctx->position,
+		&fdi_ctx->rule, sizeof(struct rule));
+
+	HAL_INFO(("rule=%08lx, rule_size=%d, rtype=%d",
+		fdi_ctx->position, fdi_ctx->rule.rule_size, fdi_ctx->rule.rtype));
+
+	HAL_INFO(("  jump_position=%08lx", fdi_ctx->rule.jump_position));
+
+	HAL_INFO(("  key_len=%d, key_offset=%08lx",
+		fdi_ctx->rule.key_len, fdi_ctx->position + offsetof(struct rule, key)));
+
+	HAL_INFO(("  value_len=%d, value_offset=%08lx",
+		fdi_ctx->rule.value_len, fdi_ctx->rule.value_offset));
+
+	init_rule_struct(&fdi_ctx->rule);
+}
+
+static void remember_jump_position(struct fdi_context *fdi_ctx)
+{
+	if (fdi_ctx->depth >= HAL_MAX_INDENT_DEPTH)
+		DIE(("Rule depth overflow"));
+	fdi_ctx->match_at_depth[fdi_ctx->depth++] = fdi_ctx->position;
+};
+
+static void set_jump_position(struct fdi_context *fdi_ctx)
+{
+	off_t	offset;
+
+	if (fdi_ctx->depth <= 0)
+		DIE(("Rule depth underrun"));
+
+	fdi_ctx->depth--;
+	offset = lseek(fdi_ctx->cache_fd, 0, SEEK_END);
+	pad32_write(fdi_ctx->cache_fd,
+		fdi_ctx->match_at_depth[fdi_ctx->depth] + offsetof(struct rule, jump_position),
+		&offset, sizeof(fdi_ctx->rule.jump_position));
+
+	HAL_INFO(("modify rule=0x%08x, set jump to 0x%08x",
+		fdi_ctx->match_at_depth[fdi_ctx->depth], offset));
+}
+
+/* expat cb for start, e.g. <match foo=bar */
+static void
+start (void *data, const char *el, const char **attr)
+{
+	struct fdi_context	*fdi_ctx = data;
+	int			i;
+
+	/* we found a new tag, but old rule was not saved yet */
+	if (fdi_ctx->rule.rtype != RULE_UNKNOWN)
+		store_rule(fdi_ctx);
+
+	init_rule_struct(&fdi_ctx->rule);
+	fdi_ctx->rule.rtype = get_rule_type(el);
+	fdi_ctx->position = lseek(fdi_ctx->cache_fd, 0, SEEK_END);
+	if (fdi_ctx->rule.rtype == RULE_UNKNOWN) return;
+
+	/* get key and attribute for current rule */
+	for (i = 0; attr[i] != NULL; i+=2) {
+		if (strcmp (attr[i], "key") == 0) {
+			if (fdi_ctx->rule.key_len > 0)
+				DIE(("Bad rule: key already defined"));
+
+			store_key(fdi_ctx, attr[i + 1]);
+			continue;
+		}
+		if (fdi_ctx->rule.rtype == RULE_SPAWN) {
+			if (strcmp(attr[i], "udi") == 0) {
+				if (fdi_ctx->rule.key_len > 0)
+					DIE(("Bad rule: key already defined"));
+
+				store_key(fdi_ctx, attr[i + 1]);
+				continue;
+			}
+			/* TODO: is it error ??? */
+			continue;
+		} else if (fdi_ctx->rule.rtype == RULE_MATCH) {
+
+			if (fdi_ctx->rule.key_len == 0)
+				DIE(("Bad rule: value without a key"));
+
+			fdi_ctx->rule.type_match = get_match_type(attr[i]);
+
+			if (fdi_ctx->rule.type_match == MATCH_UNKNOWN)
+				DIE(("Bad rule: unknown type_match"));
+
+			store_value(fdi_ctx, attr[i + 1], strlen(attr[i + 1]));
+			continue;
+		} else {
+			if (strcmp(attr[i], "type") != 0){
+				/* TODO: is it error ??? */
+				continue;
+			}
+			fdi_ctx->rule.type_merge = get_merge_type(attr[i + 1]);
+
+			if (fdi_ctx->rule.type_merge == MERGE_UNKNOWN)
+				DIE(("Bad rule: unknown type_merge"));
+
+			continue;
+		}
+	}
+
+	if (fdi_ctx->rule.key_len == 0)
+	    DIE(("Bad rule: key not found"));
+
+	/* match rules remember the current nesting and
+	   the label to jump to if not matching */
+	if (fdi_ctx->rule.rtype == RULE_MATCH)
+		remember_jump_position(fdi_ctx);
+
+	return;
+}
+
+/* expat cb for character data, i.e. the text data in between tags */
+static void
+cdata (void *data, const char *s, int len){
+	struct fdi_context *fdi_ctx = data;
+
+	if (fdi_ctx->rule.rtype != RULE_MERGE &&
+	    fdi_ctx->rule.rtype != RULE_PREPEND &&
+	    fdi_ctx->rule.rtype != RULE_APPEND &&
+	    fdi_ctx->rule.rtype != RULE_REMOVE &&
+	    fdi_ctx->rule.rtype != RULE_SPAWN)
+		return;
+
+	store_value(fdi_ctx, s, len);
+}
+
+/* expat cb for end, e.g. </device> */
+static void
+end (void *data, const char *el){
+	struct fdi_context *fdi_ctx = data;
+	enum rule_type rtype = get_rule_type(el);
+
+	if (rtype == RULE_UNKNOWN) return;
+	if (rtype == RULE_MATCH){
+		set_jump_position(fdi_ctx);
+		return;
+	}
+
+	/* only valid if element is in the current context */
+	if (fdi_ctx->rule.rtype != rtype){
+		DIE(("Unexpected tag '%s'", el));
+	}
+	store_rule(fdi_ctx);
+}
+
+/* decompile an fdi file into a list of rules as this is quicker than opening then each time we want to search */
+static int
+rules_add_fdi_file (const char *filename, int fd){
+	struct fdi_context	*fdi_ctx;
+	char			*buf;
+	gsize			buflen;
+	int			rc;
+
+	if (!g_file_get_contents (filename, &buf, &buflen, NULL)) return -1;
+
+	/* create new context */
+	fdi_ctx = g_new0 (struct fdi_context, 1);
+	memset(fdi_ctx, 0, sizeof(struct fdi_context));
+	init_rule_struct(&fdi_ctx->rule);
+	fdi_ctx->cache_fd = fd;
+
+	XML_Parser parser = XML_ParserCreate (NULL);
+	if (parser == NULL) {
+		fprintf (stderr, "Couldn't allocate memory for parser\n");
+		return -1;
+	}
+	XML_SetUserData (parser, fdi_ctx);
+	XML_SetElementHandler (parser, start, end);
+	XML_SetCharacterDataHandler (parser, cdata);
+	rc = XML_Parse (parser, buf, buflen, 1);
+	if (rc == 0)
+		fprintf (stderr, "Parse error at line %i:\n%s\n",
+			(int) XML_GetCurrentLineNumber (parser),
+			XML_ErrorString (XML_GetErrorCode (parser)));
+	XML_ParserFree (parser);
+	g_free (buf);
+
+	/* insert last dummy rule into list */
+	init_rule_struct(&fdi_ctx->rule);
+	fdi_ctx->rule.rtype = RULE_EOF;
+	fdi_ctx->position = lseek(fdi_ctx->cache_fd, 0, SEEK_END);
+	store_key(fdi_ctx, filename);
+	store_value(fdi_ctx, "", 0);
+	store_rule(fdi_ctx);
+
+	g_free (fdi_ctx);
+
+	if (rc == 0) return -1;
+	return lseek(fd, 0, SEEK_END);
+}
+
+
+/* recurse a directory tree, searching and adding fdi files */
+static int
+rules_search_and_add_fdi_files (const char *dir, int fd)
+{
+	int i;
+	int num_entries;
+	struct dirent **name_list;
+	
+	num_entries = scandir (dir, &name_list, 0, _alphasort);
+	if (num_entries == -1)
+		return -1;
+
+	for (i = num_entries - 1; i >= 0; i--) {
+		int len;
+		char *filename;
+		gchar *full_path;
+
+		filename = name_list[i]->d_name;
+		len = strlen (filename);
+		full_path = g_strdup_printf ("%s/%s", dir, filename);
+		if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
+			if (len >= 5 && strcmp(&filename[len - 4], ".fdi") == 0) {
+				int fdi_rules_size;
+				fdi_rules_size = rules_add_fdi_file (full_path, fd);
+				if (fdi_rules_size < 0)
+					HAL_WARNING (("error processing fdi file '%s'", full_path));
+			}
+		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR)) && filename[0] != '.') {
+			int num_bytes;
+			char *dirname;
+
+			num_bytes = len + strlen (dir) + 1 + 1;
+			dirname = (char *) malloc (num_bytes);
+			if (dirname == NULL)
+				break;
+
+			snprintf (dirname, num_bytes, "%s/%s", dir, filename);
+			rules_search_and_add_fdi_files (dirname, fd);
+			free (dirname);
+		}
+		g_free (full_path);
+		free (name_list[i]);
+	}
+
+	for (; i >= 0; i--) free (name_list[i]);
+	free (name_list);
+	return 0;
+}
+
+
+int haldc_is_verbose = 0;
+int haldc_use_syslog = 0;
+int haldc_force_recreate = 0;
+
+
+static void
+di_rules_init (void)
+{
+	char * cachename;
+	int fd;
+	struct cache_header header;
+	struct stat st;
+	char namebuf [PATH_MAX+1];
+	char *hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
+	char *hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
+	char *hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
+
+	cachename = getenv ("HAL_FDI_CACHE_NAME");
+	if(!cachename) cachename = CACHE_FILE;
+	HAL_INFO (("Loading rules"));
+	
+	if(haldc_force_recreate && !stat(cachename, &st)) {
+	    strncpy(namebuf, cachename, PATH_MAX);
+	    strncat(namebuf, ".bak", PATH_MAX);
+	    if(rename(cachename, namebuf) < 0)
+		DIE(("Unable to rename file %s to file %s", cachename, namebuf));
+	}
+
+	fd = open(cachename, O_CREAT|O_RDWR|O_EXCL|O_TRUNC, 0644);
+	if(fd < 0) DIE(("Unable to open fdi cache %s file for writing: %s",
+		cachename, strerror(errno)));
+
+	memset(&header, 0, sizeof(struct cache_header));
+	pad32_write(fd, 0, &header, sizeof(struct cache_header));
+
+	header.fdi_rules_preprobe = lseek(fd, 0, SEEK_END);
+	if (hal_fdi_source_preprobe != NULL)
+		rules_search_and_add_fdi_files (hal_fdi_source_preprobe, fd);
+	else{
+		rules_search_and_add_fdi_files (PACKAGE_DATA_DIR "/hal/fdi/preprobe", fd);
+		rules_search_and_add_fdi_files (PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe", fd);
+	}
+
+	header.fdi_rules_information = lseek(fd, 0, SEEK_END);
+	if (hal_fdi_source_information != NULL)
+		rules_search_and_add_fdi_files (hal_fdi_source_information, fd);
+	else {
+		rules_search_and_add_fdi_files (PACKAGE_DATA_DIR "/hal/fdi/information", fd);
+		rules_search_and_add_fdi_files (PACKAGE_SYSCONF_DIR "/hal/fdi/information", fd);
+	}
+
+	header.fdi_rules_policy = lseek(fd, 0, SEEK_END);
+	if (hal_fdi_source_policy != NULL)
+		rules_search_and_add_fdi_files (hal_fdi_source_policy, fd);
+	else {
+		rules_search_and_add_fdi_files (PACKAGE_DATA_DIR "/hal/fdi/policy", fd);
+		rules_search_and_add_fdi_files (PACKAGE_SYSCONF_DIR "/hal/fdi/policy", fd);
+	}
+
+	header.all_rules_size = lseek(fd, 0, SEEK_END);
+	pad32_write(fd, 0, &header, sizeof(struct cache_header));
+	close(fd);
+	
+	HAL_INFO(("preprobe: offset=%08lx, size=%d", header.fdi_rules_preprobe,
+		header.fdi_rules_information - header.fdi_rules_preprobe));
+	HAL_INFO(("information: offset=%08lx, size=%d", header.fdi_rules_information,
+		header.fdi_rules_policy - header.fdi_rules_information));
+	HAL_INFO(("policy: offset=%08lx, size=%d", header.fdi_rules_policy,
+		header.all_rules_size - header.fdi_rules_policy));
+
+	HAL_INFO (("Loading rules done (occupying %d bytes)", header.all_rules_size));
+}
+
+/** Print out program usage.
+ *
+ */
+static void
+usage ()
+{
+	fprintf (stderr, "\n" "usage : hald-cache [--verbose=yes|no] [--use-syslog] [--help]\n");
+	fprintf (stderr,
+		 "\n"
+		 "        --verbose=yes|no      Print out debug (overrides HALD_VERBOSE).\n"
+ 		 "        --use-syslog          Print out debug messages to syslog instead of\n"
+		 "                              stderr. Use this option to get debug messages.\n"
+		 "        --force               Force regeneration of cache.\n"
+		 "        --help                Show this information and exit.\n"
+		 "        --version             Output version information and exit.\n"
+		 "\n"
+		 "hald-cache is a tool to generate binary cache from FDI files.\n"
+		 "\n"
+		 "For more information visit http://freedesktop.org/Software/hal\n"
+		 "\n");
+}
+
+
+int main(int argc, char * argv[])
+{
+	openlog ("hald-cache", LOG_PID, LOG_DAEMON);
+
+	while (1) {
+		int c;
+		int option_index = 0;
+		const char *opt;
+		static struct option long_options[] = {
+			{"exit-after-probing", 0, NULL, 0},
+			{"daemon", 1, NULL, 0},
+			{"verbose", 1, NULL, 0},
+			{"retain-privileges", 0, NULL, 0},
+			{"use-syslog", 0, NULL, 0},
+			{"help", 0, NULL, 0},
+			{"version", 0, NULL, 0},
+			{"force", 0, NULL, 0},
+			{NULL, 0, NULL, 0}
+		};
+
+		c = getopt_long (argc, argv, "",
+				 long_options, &option_index);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 0:
+			opt = long_options[option_index].name;
+
+			if (strcmp (opt, "help") == 0) {
+				usage ();
+				return 0;
+			} else if (strcmp (opt, "version") == 0) {
+				fprintf (stderr, "HAL package version: " PACKAGE_VERSION "\n");
+				return 0;
+			} else if (strcmp (opt, "verbose") == 0) {
+				if (strcmp ("yes", optarg) == 0) {
+					haldc_is_verbose = 1;
+				} else if (strcmp ("no", optarg) == 0) {
+					haldc_is_verbose = 0;
+				} else {
+					usage ();
+					return 1;
+				}
+			} else if (strcmp (opt, "use-syslog") == 0) {
+                                haldc_use_syslog = 1;
+			} else if (strcmp (opt, "force") == 0) {
+                                haldc_force_recreate = 1;
+			}
+
+			break;
+
+		default:
+			usage ();
+			return 1;
+			break;
+		}
+	}
+
+	if (haldc_is_verbose)
+		logger_enable ();
+	else
+		logger_disable ();
+
+	if (haldc_use_syslog)
+		logger_enable_syslog ();
+	else
+		logger_disable_syslog ();
+
+	di_rules_init();
+	return 0;
+}
diff --git a/hald/device_info.c b/hald/device_info.c
index e6077f8..0dd1cdc 100644
--- a/hald/device_info.c
+++ b/hald/device_info.c
@@ -1,11 +1,13 @@
 /***************************************************************************
  * CVSID: $Id$
  *
- * device_store.c : Parse .fdi files and match/merge device properties.
+ * device_info.c : match/merge device properties.
  *
  * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
  * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
  * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
  *
  * Licensed under the Academic Free License version 2.1
  *
@@ -38,246 +40,24 @@
 #include <assert.h>
 #include <dbus/dbus.h>
 #include <dbus/dbus-glib.h>
+#include <sys/types.h>
 #include <sys/stat.h>
+#include <fcntl.h>
 #include <math.h>
+#include <sys/mman.h>
+#include <errno.h>
 
 #include "hald.h"
 #include "logger.h"
+#include "mmap_cache.h"
 #include "device_info.h"
 #include "device_store.h"
 #include "util.h"
+#include "rule.h"
 
-#define MAX_INDENT_DEPTH		64
+void	*rules_ptr = NULL;
 
-/* pre-parsed rules to keep in memory */
-static GSList *fdi_rules_preprobe;
-static GSList *fdi_rules_information;
-static GSList *fdi_rules_policy;
-
-/* rule type to process */
-enum rule_type {
-	RULE_UNKNOWN,
-	RULE_MATCH,
-	RULE_MERGE,
-	RULE_APPEND,
-	RULE_PREPEND,
-	RULE_REMOVE,
-	RULE_CLEAR,
-	RULE_SPAWN,
-	RULE_EOF,
-};
-
-/* type of merge command */
-enum merge_type {
-	MERGE_UNKNOWN,
-	MERGE_STRING,
-	MERGE_BOOLEAN,
-	MERGE_INT32,
-	MERGE_UINT64,
-	MERGE_DOUBLE,
-	MERGE_COPY_PROPERTY,
-	MERGE_STRLIST,
-	MERGE_REMOVE,
-};
-
-/* type of match command */
-enum
-match_type {
-	MATCH_UNKNOWN,
-	MATCH_STRING,
-	MATCH_INT,
-	MATCH_UINT64,
-	MATCH_BOOL,
-	MATCH_EXISTS,
-	MATCH_EMPTY,
-	MATCH_ISASCII,
-	MATCH_IS_ABS_PATH,
-	MATCH_CONTAINS,
-	MATCH_CONTAINS_NCASE,
-	MATCH_PREFIX,
-	MATCH_PREFIX_NCASE,
-	MATCH_SUFFIX,
-	MATCH_SUFFIX_NCASE,
-	MATCH_COMPARE_LT,
-	MATCH_COMPARE_LE,
-	MATCH_COMPARE_GT,
-	MATCH_COMPARE_GE,
-};
-
-/* a "rule" structure that is a generic node of the fdi file */
-struct rule {
-	/* typ of tule in the list */
-	enum rule_type rtype;
-
-	/* all rules have a key */
-	char *key;
-
-	/* "match" or "merge" rule */
-	enum match_type type_match;
-	enum merge_type type_merge;
-
-	char *value;
-	int value_len;
-
-	/* if rule does not match, skip to this rule */
-	struct rule *next_rule;
-};
-
-/* ctx of the current fdi file used for parsing */
-struct fdi_context {
-	int depth;
-	struct rule *match_at_depth[MAX_INDENT_DEPTH];
-
-	/* current rule */
-	struct rule *rule;
-
-	int fdi_rule_size;
-
-	/* all rules */
-	GSList* rules;
-};
-
-static enum
-rule_type get_rule_type (const char *str)
-{
-	if (strcmp (str, "match") == 0)
-		return RULE_MATCH;
-	if (strcmp (str, "merge") == 0)
-		return RULE_MERGE;
-	if (strcmp (str, "append") == 0)
-		return RULE_APPEND;
-	if (strcmp (str, "prepend") == 0)
-		return RULE_PREPEND;
-	if (strcmp (str, "remove") == 0)
-		return RULE_REMOVE;
-	if (strcmp (str, "clear") == 0)
-		return RULE_CLEAR;
-	if (strcmp (str, "spawn") == 0)
-		return RULE_SPAWN;
-	return RULE_UNKNOWN;
-}
-
-/*
-static char *
-get_rule_type_str (enum rule_type type)
-{
-	switch (type) {
-	case RULE_MATCH:
-		return "match";
-	case RULE_MERGE:
-		return "merge";
-	case RULE_APPEND:
-		return "append";
-	case RULE_PREPEND:
-		return "prepend";
-	case RULE_REMOVE:
-		return "remove";
-	case RULE_CLEAR:
-		return "clear";
-	case RULE_SPAWN:
-		return "spawn";
-	case RULE_EOF:
-		return "eof";
-	case RULE_UNKNOWN:
-		return "unknown rule type";
-	}
-	return "invalid rule type";
-}
-*/
-
-static enum
-merge_type get_merge_type (const char *str)
-{
-	if (strcmp (str, "string") == 0)
-		return MERGE_STRING;
-	if (strcmp (str, "bool") == 0)
-		return MERGE_BOOLEAN;
-	if (strcmp (str, "int") == 0)
-		return MERGE_INT32;
-	if (strcmp (str, "unint64") == 0)
-		return MERGE_UINT64;
-	if (strcmp (str, "double") == 0)
-		return MERGE_DOUBLE;
-	if (strcmp (str, "strlist") == 0)
-		return MERGE_STRLIST;
-	if (strcmp (str, "copy_property") == 0)
-		return MERGE_COPY_PROPERTY;
-	if (strcmp (str, "remove") == 0)
-		return MERGE_REMOVE;
-	return MERGE_UNKNOWN;
-}
-
-/*
-static char *
-get_merge_type_str (enum merge_type type)
-{
-	switch (type) {
-	case MERGE_STRING:
-		return "string";
-	case MERGE_BOOLEAN:
-		return "bool";
-	case MERGE_INT32:
-		return "int";
-	case MERGE_UINT64:
-		return "unint64";
-	case MERGE_DOUBLE:
-		return "double";
-	case MERGE_STRLIST:
-		return "strlist";
-	case MERGE_COPY_PROPERTY:
-		return "copy_property";
-	case MERGE_REMOVE:
-		return "remove";
-	case MERGE_UNKNOWN:
-		return "unknown merge type";
-	}
-	return "invalid merge type";
-}
-*/
-
-static enum
-match_type get_match_type(const char *str)
-{
-	if (strcmp (str, "string") == 0)
-		return MATCH_STRING;
-	if (strcmp (str, "int") == 0)
-		return MATCH_INT;
-	if (strcmp (str, "uint64") == 0)
-		return MATCH_UINT64;
-	if (strcmp (str, "bool") == 0)
-		return MATCH_BOOL;
-	if (strcmp (str, "exists") == 0)
-		return MATCH_EXISTS;
-	if (strcmp (str, "empty") == 0)
-		return MATCH_EMPTY;
-	if (strcmp (str, "is_ascii") == 0)
-		return MATCH_ISASCII;
-	if (strcmp (str, "is_absolute_path") == 0)
-		return MATCH_IS_ABS_PATH;
-	if (strcmp (str, "contains") == 0)
-		return MATCH_CONTAINS;
-	if (strcmp (str, "contains_ncase") == 0)
-		return MATCH_CONTAINS_NCASE;
-	if (strcmp (str, "prefix") == 0)
-		return MATCH_PREFIX;
-	if (strcmp (str, "prefix_ncase") == 0)
-		return MATCH_PREFIX_NCASE;
-	if (strcmp (str, "suffix") == 0)
-		return MATCH_SUFFIX;
-	if (strcmp (str, "suffix_ncase") == 0)
-		return MATCH_SUFFIX_NCASE;
-	if (strcmp (str, "compare_lt") == 0)
-		return MATCH_COMPARE_LT;
-	if (strcmp (str, "compare_le") == 0)
-		return MATCH_COMPARE_LE;
-	if (strcmp (str, "compare_gt") == 0)
-		return MATCH_COMPARE_GT;
-	if (strcmp (str, "compare_ge") == 0)
-		return MATCH_COMPARE_GE;
-	return MATCH_UNKNOWN;
-}
-
-/*
+#ifdef DUMP_RULES
 static char *
 get_match_type_str (enum match_type type)
 {
@@ -323,7 +103,7 @@ get_match_type_str (enum match_type type
 	}
 	return "invalid match type";
 }
-*/
+#endif
 
 /** Resolve a udi-property path as used in .fdi files.
  *
@@ -475,7 +255,7 @@ handle_match (struct rule *rule, HalDevi
 	char udi_to_check[HAL_PATH_MAX];
 	char prop_to_check[HAL_PATH_MAX];
 	const char *key = rule->key;
-	const char *value = rule->value;
+	const char *value = (char *)RULES_PTR(rule->value_offset);
 
 	/* Resolve key paths like 'someudi/foo/bar/baz:prop.name' '@prop.here.is.an.udi:with.prop.name' */
 	if (!resolve_udiprop_path (key,
@@ -870,7 +650,7 @@ static gboolean
 handle_merge (struct rule *rule, HalDevice *d)
 {
 	const char *key = rule->key;
-	const char *value = rule->value;
+	const char *value = (char *)RULES_PTR(rule->value_offset);
 
 	if (rule->rtype == RULE_MERGE) {
 
@@ -1044,375 +824,53 @@ handle_merge (struct rule *rule, HalDevi
 	return TRUE;
 }
 
-/* for each node, free memory and it's own list of nodes */
-static void
-rules_cleanup_list (GSList *fdi_rules)
-{
-	GSList *elem;
-
-	for (elem = fdi_rules; elem != NULL; elem = g_slist_next (elem)) {
-		struct rule *rule = elem->data;
-
-		g_free (rule->key);
-		g_free (rule->value);
-		g_free (rule);
-	}
-	g_slist_free (fdi_rules);
-	fdi_rules = NULL;
-}
-
-/* expat cb for start, e.g. <match foo=bar */
-static void
-start (void *data, const char *el, const char **attr)
-{
-	struct fdi_context *fdi_ctx = data;
-	enum rule_type rtype = get_rule_type (el);
-	int i;
-
-	if (rtype == RULE_UNKNOWN)
-		return;
-
-	if (fdi_ctx->rule == NULL)
-		return;
-
-	/* get key and attribute for current rule */
-	for (i = 0; attr[i] != NULL; i+=2) {
-		if (strcmp (attr[i], "key") == 0) {
-			fdi_ctx->rule->key = g_strdup (attr[1]);
-			continue;
-		}
-		if (rtype == RULE_SPAWN) {
-			if (strcmp (attr[i], "udi") == 0) {
-				fdi_ctx->rule->key = g_strdup (attr[1]);
-				continue;
-			}
-		} else if (rtype == RULE_MATCH) {
-			fdi_ctx->rule->type_match = get_match_type (attr[i]);
-			if (fdi_ctx->rule->type_match == MATCH_UNKNOWN)
-				continue;
-			fdi_ctx->rule->value = g_strdup (attr[i+1]);
-		} else {
-			if (strcmp (attr[i], "type") != 0)
-				continue;
-			fdi_ctx->rule->type_merge = get_merge_type (attr[i+1]);
-			if (fdi_ctx->rule->type_merge == MERGE_UNKNOWN)
-				return;
-		}
-	}
-
-	if (fdi_ctx->rule->key[0] == '\0')
-		return;
-
-	fdi_ctx->rule->rtype = rtype;
-
-	/* match rules remember the current nesting and the label to jump to if not matching */
-	if (rtype == RULE_MATCH) {
-		/* remember current nesting */
-		fdi_ctx->depth++;
-
-		/* remember rule at nesting level nesting */
-		fdi_ctx->match_at_depth[fdi_ctx->depth] = fdi_ctx->rule;
-
-		/* insert match rule into list and get new rule */
-		fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
-		fdi_ctx->rule = g_new0 (struct rule ,1);
-	}
-}
-
-/* expat cb for character data, i.e. the text data in between tags */
-static void
-cdata (void *data, const char *s, int len)
-{
-	struct fdi_context *fdi_ctx = data;
-
-	if (fdi_ctx->rule == NULL)
-		return;
-
-	if (fdi_ctx->rule->rtype != RULE_MERGE &&
-	    fdi_ctx->rule->rtype != RULE_PREPEND &&
-	    fdi_ctx->rule->rtype != RULE_APPEND &&
-	    fdi_ctx->rule->rtype != RULE_REMOVE &&
-	    fdi_ctx->rule->rtype != RULE_SPAWN)
-		return;
-
-	if (len < 1)
-		return;
-
-	/* copy cdata in current context */
-	fdi_ctx->rule->value = g_realloc (fdi_ctx->rule->value, fdi_ctx->rule->value_len + len+1);
-	memcpy (&fdi_ctx->rule->value[fdi_ctx->rule->value_len], s, len);
-	fdi_ctx->rule->value_len += len;
-	fdi_ctx->rule->value[fdi_ctx->rule->value_len] = '\0';
-}
-
-/* expat cb for end, e.g. </device> */
-static void
-end (void *data, const char *el)
-{
-	struct fdi_context *fdi_ctx = data;
-	enum rule_type rtype = get_rule_type (el);
-
-	if (rtype == RULE_UNKNOWN)
-		return;
-
-	if (rtype == RULE_MATCH) {
-		if (fdi_ctx->depth <= 0)
-			return;
-
-		/* get corresponding match rule and set the rule to skip to */
-		fdi_ctx->match_at_depth[fdi_ctx->depth]->next_rule = fdi_ctx->rule;
-		fdi_ctx->depth--;
-		return;
-	}
-
-	/* only valid if element is in the current context */
-	if (fdi_ctx->rule->rtype != rtype)
-		return;
-
-	/* set empty value to empty string */
-	if (fdi_ctx->rule->value == NULL)
-		fdi_ctx->rule->value = g_strdup ("");
-
-	if (fdi_ctx->fdi_rule_size >= 0) {
-		fdi_ctx->fdi_rule_size += 
-			sizeof (struct rule) + 
-			strlen (fdi_ctx->rule->key) + 
-			fdi_ctx->rule->value_len;
-	}
-
-	/* insert merge rule into list and get new rule */
-	fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
-	fdi_ctx->rule = g_new0 (struct rule, 1);
-}
-
-/* decompile an fdi file into a list of rules as this is quicker than opening then each time we want to search */
-static int
-rules_add_fdi_file (GSList **fdi_rules, const char *filename, gboolean compute_rule_size)
-{
-	struct fdi_context *fdi_ctx;
-	char *buf;
-	gsize buflen;
-	int rc;
-	int fdi_rule_size;
-
-	if (!g_file_get_contents (filename, &buf, &buflen, NULL))
-		return -1;
-
-	/* get context and first rule */
-	fdi_ctx = g_new0 (struct fdi_context ,1);
-	fdi_ctx->rule = g_new0 (struct rule ,1);
-	fdi_ctx->fdi_rule_size = compute_rule_size ? 0 : -1;
-
-	XML_Parser parser = XML_ParserCreate (NULL);
-	if (parser == NULL) {
-		fprintf (stderr, "Couldn't allocate memory for parser\n");
-		return -1;
-	}
-	XML_SetUserData (parser, fdi_ctx);
-	XML_SetElementHandler (parser, start, end);
-	XML_SetCharacterDataHandler (parser, cdata);
-	rc = XML_Parse (parser, buf, buflen, 1);
-	if (rc == 0)
-		fprintf (stderr, "Parse error at line %i:\n%s\n",
-			(int) XML_GetCurrentLineNumber (parser),
-			XML_ErrorString (XML_GetErrorCode (parser)));
-	XML_ParserFree (parser);
-	g_free (buf);
-
-	/* insert last dummy rule into list */
-	fdi_ctx->rule->rtype = RULE_EOF;
-	fdi_ctx->rule->key = g_strdup (filename);
-	fdi_ctx->rules = g_slist_append (fdi_ctx->rules, fdi_ctx->rule);
-
-	/* add rules to external list */
-	if (rc == 0)
-		rules_cleanup_list (fdi_ctx->rules);
-	else
-		*fdi_rules = g_slist_concat (*fdi_rules, fdi_ctx->rules);
-
-	fdi_rule_size = (gint64) fdi_ctx->fdi_rule_size;
-
-	g_free (fdi_ctx);
-
-	if (rc == 0)
-		return -1;
-
-	return compute_rule_size ? fdi_rule_size : 0;
-}
-
-/* modified alphasort to count downwards */
-static int
-#ifdef __GLIBC__
-_alphasort(const void *a, const void *b)
-#else
-_alphasort(const struct dirent **a, const struct dirent **b)
-#endif
-{
-	return -alphasort (a, b);
-}
-
-/* recurse a directory tree, searching and adding fdi files */
-static int
-rules_search_and_add_fdi_files (GSList **fdi_rules, const char *dir, int *rules_size)
-{
-	int i;
-	int num_entries;
-	struct dirent **name_list;
-
-	num_entries = scandir (dir, &name_list, 0, _alphasort);
-	if (num_entries == -1)
-		return -1;
-
-	for (i = num_entries - 1; i >= 0; i--) {
-		int len;
-		char *filename;
-		gchar *full_path;
-
-		filename = name_list[i]->d_name;
-		len = strlen (filename);
-		full_path = g_strdup_printf ("%s/%s", dir, filename);
-		if (g_file_test (full_path, (G_FILE_TEST_IS_REGULAR))) {
-			if (len >= 5 && strcmp(&filename[len - 4], ".fdi") == 0) {
-				int fdi_rules_size;
-				fdi_rules_size = rules_add_fdi_file (fdi_rules, full_path, rules_size != NULL);
-				if (fdi_rules_size >= 0) {
-					if (rules_size != NULL) {
-						*rules_size += fdi_rules_size;
-						HAL_INFO (("fdi file '%s' -> %d bytes of rules", 
-							   full_path, fdi_rules_size));
-					}
-				} else {
-					HAL_WARNING (("error processing fdi file '%s'", full_path));
-				}
-			}
-		} else if (g_file_test (full_path, (G_FILE_TEST_IS_DIR)) && filename[0] != '.') {
-			int num_bytes;
-			char *dirname;
-
-			num_bytes = len + strlen (dir) + 1 + 1;
-			dirname = (char *) malloc (num_bytes);
-			if (dirname == NULL)
-				break;
-
-			snprintf (dirname, num_bytes, "%s/%s", dir, filename);
-			rules_search_and_add_fdi_files (fdi_rules, dirname, rules_size);
-			free (dirname);
-		}
-		g_free (full_path);
-		free (name_list[i]);
-	}
-
-	for (; i >= 0; i--) {
-		free (name_list[i]);
-	}
-
-	free (name_list);
-	return 0;
-}
-
-/* print the rules to screen, mainly useful for debugging */
-#if 0
-static void
-rules_dump (GSList *fdi_rules)
-{
-	GSList *elem;
-
-	for (elem = fdi_rules; elem != NULL; elem = g_slist_next (elem)) {
-		struct rule *rule = elem->data;
-
-		if (rule->rtype == RULE_EOF) {
-			printf ("%p: eof %s\n", rule, rule->key);
-		} else if (rule->rtype == RULE_MATCH) {
-			printf ("\n");
-			printf ("%p: match '%s' (%s) '%s' (skip to %p)\n",
-				rule, rule->key, get_match_type_str (rule->type_match),
-				rule->value, rule->next_rule);
-		} else {
-			printf ("%p: %s '%s' (%s) '%s'\n",
-				rule, get_rule_type_str (rule->rtype), rule->key,
-				get_merge_type_str (rule->type_merge), rule->value);
-		}
+static struct rule *di_next(struct rule *rule){
+	struct cache_header	*header = (struct cache_header*) RULES_PTR(0);
+	size_t			offset = (char *)rule - (char*)rules_ptr;
+	size_t			next = offset + rule->rule_size;
+
+	if (((offset >= header->fdi_rules_preprobe) && (next < header->fdi_rules_information)) ||
+	    ((offset >= header->fdi_rules_information) && (next < header->fdi_rules_policy)) ||
+	    ((offset >= header->fdi_rules_policy) && (next < header->all_rules_size))){
+		return (struct rule*) RULES_PTR(next);
+	}
+	return NULL;
+}
+
+static struct rule *di_jump(struct rule *rule){
+	struct cache_header	*header = (struct cache_header*) RULES_PTR(0);
+	size_t			offset = (char *)rule - (char*)rules_ptr;
+	size_t			next = rule->jump_position;
+
+	if (next == 0) return NULL;
+	if (((offset >= header->fdi_rules_preprobe) && (next < header->fdi_rules_information)) ||
+	    ((offset >= header->fdi_rules_information) && (next < header->fdi_rules_policy)) ||
+	    ((offset >= header->fdi_rules_policy) && (next < header->all_rules_size))){
+		return (struct rule*) RULES_PTR(next);
 	}
+	return NULL;
 }
-#endif
-
-/* setup the location of the rules */
-void
-di_rules_init (void)
-{
-	int size;
-	char *hal_fdi_source_preprobe = getenv ("HAL_FDI_SOURCE_PREPROBE");
-	char *hal_fdi_source_information = getenv ("HAL_FDI_SOURCE_INFORMATION");
-	char *hal_fdi_source_policy = getenv ("HAL_FDI_SOURCE_POLICY");
-
-	HAL_INFO (("Loading rules"));
-
-	size = 0;
-
-	if (hal_fdi_source_preprobe != NULL)
-		rules_search_and_add_fdi_files (&fdi_rules_preprobe, hal_fdi_source_preprobe, &size);
-	else {
-		rules_search_and_add_fdi_files (&fdi_rules_preprobe, PACKAGE_DATA_DIR "/hal/fdi/preprobe", &size);
-		rules_search_and_add_fdi_files (&fdi_rules_preprobe, PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe", &size);
-	}
-
-	if (hal_fdi_source_information != NULL)
-		rules_search_and_add_fdi_files (&fdi_rules_information, hal_fdi_source_information, &size);
-	else {
-		rules_search_and_add_fdi_files (&fdi_rules_information, PACKAGE_DATA_DIR "/hal/fdi/information", &size);
-		rules_search_and_add_fdi_files (&fdi_rules_information, PACKAGE_SYSCONF_DIR "/hal/fdi/information", &size);
-	}
-
-	if (hal_fdi_source_policy != NULL)
-		rules_search_and_add_fdi_files (&fdi_rules_policy, hal_fdi_source_policy, &size);
-	else {
-		rules_search_and_add_fdi_files (&fdi_rules_policy, PACKAGE_DATA_DIR "/hal/fdi/policy", &size);
-		rules_search_and_add_fdi_files (&fdi_rules_policy, PACKAGE_SYSCONF_DIR "/hal/fdi/policy", &size);
-	}
 
-	/* dump the rules (commented out as this is expensive) */
-	/*rules_dump (fdi_rules_preprobe);
-	  rules_dump (fdi_rules_information);
-	  rules_dump (fdi_rules_policy);
-	*/
-
-	HAL_INFO (("Loading rules done (occupying %d bytes)", size));
-}
-
-/* cleanup the rules */
-void
-di_rules_cleanup (void)
-{
-	rules_cleanup_list (fdi_rules_preprobe);
-	rules_cleanup_list (fdi_rules_information);
-	rules_cleanup_list (fdi_rules_policy);
-	fdi_rules_preprobe = NULL;
-	fdi_rules_information = NULL;
-	fdi_rules_policy = NULL;
-}
 
 /* process a match and merge comand for a device */
 static void
-rules_match_and_merge_device (GSList *fdi_rules, HalDevice *d)
+rules_match_and_merge_device (void *fdi_rules_list, HalDevice *d)
 {
-	GSList *elem;
-
-	if (fdi_rules == NULL) {
-		di_rules_cleanup ();
-		di_rules_init ();
-	}
-
-	elem = fdi_rules;
-	while (elem != NULL) {
-		struct rule *rule = elem->data;
+	struct rule *rule = fdi_rules_list;
+	while (rule != NULL){
+		HAL_INFO(("== Iterating rules =="));
 
 		switch (rule->rtype) {
 		case RULE_MATCH:
 			/* skip non-matching rules block */
-			/*HAL_INFO(("%p match '%s' at %s", rule, rule->key, hal_device_get_udi (d)));*/
+			HAL_INFO(("%p match '%s' at %s", rule, rule->key, hal_device_get_udi (d)));
 			if (!handle_match (rule, d)) {
-				/*HAL_INFO(("no match, skip to rule %s (%p)", get_rule_type_str (rule->next_rule->rtype), rule->next_rule));*/
-				elem = g_slist_find (elem, rule->next_rule);
+				HAL_INFO(("no match, skip to rule (%llx)", rule->jump_position));
+				rule = di_jump(rule);
+
+				if(rule == NULL)
+					DIE(("Rule is NULL on jump"));
+
 				continue;
 			}
 			break;
@@ -1435,28 +893,48 @@ rules_match_and_merge_device (GSList *fd
 			HAL_WARNING(("Unhandled rule (%i)!", rule->rtype));
 			break;
 		}
-		elem = g_slist_next (elem);
+		rule = di_next(rule);
 	}
 }
 
+
 /* merge the device info type, either preprobe, info or policy */
 gboolean
-di_search_and_merge (HalDevice *d, DeviceInfoType type)
-{
+di_search_and_merge (HalDevice *d, DeviceInfoType type){
+	struct cache_header	*header = (struct cache_header*) RULES_PTR(0);
+
 	switch (type) {
 	case DEVICE_INFO_TYPE_PREPROBE:
-		/*HAL_INFO(("apply fdi preprobe to device %p", d));*/
-		rules_match_and_merge_device (fdi_rules_preprobe, d);
+		/* Checking if we have at least one preprobe rule */
+		if(header->fdi_rules_information > header->fdi_rules_preprobe)
+		{
+			HAL_INFO(("preprobe rules offset: %ld", header->fdi_rules_preprobe));
+			HAL_INFO(("preprobe rules size: %ld",
+				header->fdi_rules_information - header->fdi_rules_preprobe));
+			rules_match_and_merge_device (RULES_PTR(header->fdi_rules_preprobe), d);
+		}
 		break;
 
 	case DEVICE_INFO_TYPE_INFORMATION:
-		/*HAL_INFO(("apply fdi info to device %p", d));*/
-		rules_match_and_merge_device (fdi_rules_information, d);
+		/* Checking if we have at least one information rule */
+		if(header->fdi_rules_policy > header->fdi_rules_information)
+		{
+			HAL_INFO(("information rules offset: %ld", header->fdi_rules_information));
+			HAL_INFO(("information rules size: %ld",
+				header->fdi_rules_policy - header->fdi_rules_information));
+			rules_match_and_merge_device (RULES_PTR(header->fdi_rules_information), d);
+		}
 		break;
 
 	case DEVICE_INFO_TYPE_POLICY:
-		/*HAL_INFO(("apply fdi policy to device %p", d));*/
-		rules_match_and_merge_device (fdi_rules_policy, d);
+		/* Checking if we have at least one policy rule */
+		if(header->all_rules_size > header->fdi_rules_policy)
+		{
+			HAL_INFO(("policy rules offset: %ld", header->fdi_rules_policy));
+			HAL_INFO(("policy rules size: %ld",
+				header->all_rules_size - header->fdi_rules_policy));
+			rules_match_and_merge_device (RULES_PTR(header->fdi_rules_policy), d);
+		}
 		break;
 
 	default:
diff --git a/hald/device_info.h b/hald/device_info.h
index 24b1567..3c1cbb5 100644
--- a/hald/device_info.h
+++ b/hald/device_info.h
@@ -32,6 +32,7 @@
 
 #include "device_store.h"
 
+
 typedef enum {
 	DEVICE_INFO_TYPE_PREPROBE,
 	DEVICE_INFO_TYPE_INFORMATION,
diff --git a/hald/hald.c b/hald/hald.c
index d946c4e..8c7875b 100644
--- a/hald/hald.c
+++ b/hald/hald.c
@@ -59,6 +59,7 @@
 #include "util.h"
 #include "hald_runner.h"
 #include "util_helper.h"
+#include "mmap_cache.h"
 
 static void delete_pid(void)
 {
@@ -418,6 +419,7 @@ main (int argc, char *argv[])
 	g_strlcat (newpath, PACKAGE_SCRIPT_DIR, sizeof (newpath));
 
 	setenv ("PATH", newpath, TRUE);
+	
 
 	while (1) {
 		int c;
@@ -502,6 +504,8 @@ main (int argc, char *argv[])
 	loop = g_main_loop_new (NULL, FALSE);
 
 	HAL_INFO ((PACKAGE_STRING));
+	
+	cache_coherency_check();
 
 	if (opt_become_daemon) {
 		int child_pid;
@@ -614,6 +618,9 @@ main (int argc, char *argv[])
 
 	hald_is_initialising = TRUE;
 
+	/* Init FDI files */
+	di_rules_init();
+
 	/* detect devices */
 	osspec_probe ();
 
diff --git a/hald/mmap_cache.c b/hald/mmap_cache.c
new file mode 100644
index 0000000..e43c6da
--- /dev/null
+++ b/hald/mmap_cache.c
@@ -0,0 +1,160 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * mmap_cache.c : FDI cache routines.
+ *
+ * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
+ * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#include <config.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#include <limits.h>
+#include <dirent.h>
+
+#include "logger.h"
+#include "rule.h"
+#include "mmap_cache.h"
+
+extern void *rules_ptr;
+
+void di_rules_init(void){
+	struct cache_header	*header;
+	char 			*cachename;
+	int			fd;
+	struct stat		statbuf;
+
+	cachename = getenv ("HAL_FDI_CACHE_NAME");
+	if(!cachename) cachename = CACHE_FILE;
+
+	if((fd = open(cachename, O_RDONLY)) < 0)
+		DIE(("Unable to open cache %s\n", cachename));
+
+	if(fstat(fd,&statbuf) < 0)
+		DIE(("Unable to stat cache %s\n", cachename));
+
+	rules_ptr = mmap (NULL, statbuf.st_size, PROT_READ, MAP_SHARED, fd, 0);
+	if(rules_ptr == MAP_FAILED)
+		DIE (("Couldn't mmap file '%s', errno=%d: %s", cachename, errno, strerror (errno)));
+
+	header = (struct cache_header*) rules_ptr;
+	HAL_INFO(("preprobe: offset=%08lx, size=%d", header->fdi_rules_preprobe,
+		header->fdi_rules_information - header->fdi_rules_preprobe));
+	HAL_INFO(("information: offset=%08lx, size=%d", header->fdi_rules_information,
+		header->fdi_rules_policy - header->fdi_rules_information));
+	HAL_INFO(("policy: offset=%08lx, size=%d", header->fdi_rules_policy,
+		header->all_rules_size - header->fdi_rules_policy));
+
+	close(fd);
+}
+
+static void dir_mtime(const char * path, time_t * mt)
+{
+    struct dirent **namelist;
+    struct stat st;
+    int n;
+    char cpath[PATH_MAX];
+
+    if (!stat(path, &st)) {
+	if(st.st_mtime > *mt)
+	    *mt = st.st_mtime;
+
+	if(S_ISDIR(st.st_mode)) {
+    		n = scandir(path, &namelist, 0, alphasort);
+    		if (n < 0)
+        		return;
+    		else {
+            		while(n--) {
+#ifdef HAVE_SNPRINTF
+			    snprintf(cpath, PATH_MAX, "%s/%s", path, namelist[n]->d_name);
+#else
+			    sprintf(cpath, "%s/%s", path, namelist[n]->d_name);
+#endif
+			    if(namelist[n]->d_name[0] != '.')
+				    dir_mtime(cpath, mt);
+
+                	    free(namelist[n]);
+            	    }
+            	    free(namelist);
+    		}
+	}
+    }
+}
+
+static void regen_cache(void)
+{
+    int ret;
+    struct stat st;
+    char cmd [PATH_MAX + 9];
+    char * c;
+    
+    if((c = getenv("HALD_CACHE_BIN")) == NULL) {
+	c = HALD_CACHE_BIN;
+    }
+#ifdef HAVE_SNPRINTF
+    snprintf(cmd, PATH_MAX + 8, "%s --force", c);
+#else
+    sprintf(cmd, "%s --force", c);
+#endif
+
+    if(!stat(c, &st) && (ret = system(cmd)) !=- 1) {
+
+	if(WTERMSIG(ret) == SIGQUIT || WTERMSIG(ret) == SIGINT)
+		DIE(("Cache creation was interrupted"));
+
+	if(WEXITSTATUS(ret) == 0)
+		HAL_INFO(("Subprocess terminated normally, cache generated."));
+    }
+    else {
+	HAL_INFO(("Unable to generate cache"));
+    }
+}
+void cache_coherency_check(void)
+{
+ time_t mt;
+ struct stat st;
+ dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/preprobe", &mt);
+ dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/preprobe", &mt);
+ dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/information", &mt);
+ dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/information", &mt);
+ dir_mtime(PACKAGE_DATA_DIR "/hal/fdi/policy", &mt);
+ dir_mtime(PACKAGE_SYSCONF_DIR "/hal/fdi/policy", &mt);
+ if(!stat(CACHE_FILE, &st)) {
+	if(st.st_mtime < mt) {
+		HAL_INFO(("Cache needs update"));
+		regen_cache();
+	}
+ }
+ else
+	regen_cache();
+
+ HAL_INFO(("cache mtime is %d",mt));
+}
diff --git a/hald/mmap_cache.h b/hald/mmap_cache.h
new file mode 100644
index 0000000..5c136d5
--- /dev/null
+++ b/hald/mmap_cache.h
@@ -0,0 +1,37 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * mmap_cache.h : cache routine and macros declarations.
+ *
+ * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
+ * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+
+#ifndef __MMAP_CACHE_H__
+
+void di_rules_init(void);
+void cache_coherency_check(void);
+
+#define RULES_PTR(x) ((void *)((unsigned char *) rules_ptr + x))
+#endif
diff --git a/hald/rule.h b/hald/rule.h
new file mode 100644
index 0000000..9c726d3
--- /dev/null
+++ b/hald/rule.h
@@ -0,0 +1,109 @@
+/***************************************************************************
+ * CVSID: $Id$
+ *
+ * rule.h : struct rule and surrounding helpful declarations/macros.
+ *
+ * Copyright (C) 2003 David Zeuthen, <david at fubar.dk>
+ * Copyright (C) 2006 Kay Sievers, <kay.sievers at vrfy.org>
+ * Copyright (C) 2006 Richard Hughes <richard at hughsie.com>
+ * Copyright (C) 2007 Mikhail Kshevetskiy <mikhail.kshevetskiy at gmail.com>
+ * Copyright (C) 2007 Sergey Lapin <slapinid at gmail.com>
+ *
+ * Licensed under the Academic Free License version 2.1
+ *
+ * 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., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ **************************************************************************/
+
+#ifndef __RULE_H__
+#define __RULE__H__
+
+/* rule type to process */
+enum rule_type {
+	RULE_UNKNOWN,
+	RULE_MATCH,
+	RULE_MERGE,
+	RULE_APPEND,
+	RULE_PREPEND,
+	RULE_REMOVE,
+	RULE_CLEAR,
+	RULE_SPAWN,
+	RULE_EOF,
+};
+
+/* type of merge command */
+enum merge_type {
+	MERGE_UNKNOWN,
+	MERGE_STRING,
+	MERGE_BOOLEAN,
+	MERGE_INT32,
+	MERGE_UINT64,
+	MERGE_DOUBLE,
+	MERGE_COPY_PROPERTY,
+	MERGE_STRLIST,
+	MERGE_REMOVE,
+};
+
+/* type of match command */
+enum
+match_type {
+	MATCH_UNKNOWN,
+	MATCH_STRING,
+	MATCH_INT,
+	MATCH_UINT64,
+	MATCH_BOOL,
+	MATCH_EXISTS,
+	MATCH_EMPTY,
+	MATCH_ISASCII,
+	MATCH_IS_ABS_PATH,
+	MATCH_CONTAINS,
+	MATCH_CONTAINS_NCASE,
+	MATCH_PREFIX,
+	MATCH_PREFIX_NCASE,
+	MATCH_SUFFIX,
+	MATCH_SUFFIX_NCASE,
+	MATCH_COMPARE_LT,
+	MATCH_COMPARE_LE,
+	MATCH_COMPARE_GT,
+	MATCH_COMPARE_GE,
+};
+
+/* a "rule" structure that is a generic node of the fdi file */
+struct rule {
+	size_t		rule_size;	/* offset to next rule in the list (aligned to 4 bytes) */
+	u_int32_t	jump_position;	/* the rule to jumo position (aligned to 4 bytes) */
+
+	enum rule_type	rtype;		/* type of rule */
+	enum match_type type_match;
+	enum merge_type type_merge;
+
+	u_int32_t	value_offset;	/* offset to keys value (aligned to 4 bytes) */
+	size_t		value_len;	/* length of keys value */
+
+	size_t		key_len;
+	char		key[0];
+};
+
+struct cache_header {
+	u_int32_t	fdi_rules_preprobe;
+	u_int32_t	fdi_rules_information;
+	u_int32_t	fdi_rules_policy;
+	u_int32_t	all_rules_size;
+	char		empty_string[4];
+};
+
+#define HAL_MAX_INDENT_DEPTH		64
+
+#endif


More information about the hal-commit mailing list