Mirror source tarball to openwrt.org (#2983)
[openwrt.git] / package / hotplug2 / patches / 100-svn_update.patch
index ab6bbc2..283e613 100644 (file)
@@ -1,6 +1,6 @@
 diff -urN -x .svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
 --- hotplug2-0.9/AUTHORS       2006-10-08 18:13:50.000000000 +0200
-+++ hotplug2/AUTHORS   2007-06-25 10:51:14.688225416 +0200
++++ hotplug2/AUTHORS   2007-06-30 12:59:20.459674000 +0200
 @@ -1,7 +1,11 @@
   Authors:
  ----------
@@ -9,7 +9,7 @@ diff -urN -x .svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
 +
 + Contributions:
 +----------------
-+nbd (rules override patch)
++nbd (rules override patch, various fixes, suggestions, testing etc.)
 +Tomas Janousek <tomi@nomi.cz> (Makefiles, SVN hosting)
  
   Thanks to:
@@ -28,29 +28,61 @@ diff -urN -x .svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
 +...anyone taking more than a short peek at the software.
 diff -urN -x .svn hotplug2-0.9/Changelog hotplug2/Changelog
 --- hotplug2-0.9/Changelog     2006-10-08 15:32:31.000000000 +0200
-+++ hotplug2/Changelog 2007-06-25 10:51:14.689225264 +0200
-@@ -1,3 +1,10 @@
++++ hotplug2/Changelog 2007-07-09 01:17:14.865503750 +0200
+@@ -1,3 +1,13 @@
 +0.9 - 1.0:
 +* Add --set-rules-file.
 +* Allow any ACTION.
 +* Add 'printdebug' rule.
 +* Fix chmod, chown, chgrp.
 +* Use octal for chmod and makedev.
++* Add 'nothrottle' flag, allowing overriding max-children from a rule
++* Various bugfixes
++* Code comments
 +
  0.8 - 0.9:
  * Use signals to handle children.
  * Separate info and debugging output.
-@@ -44,4 +51,4 @@
+@@ -44,4 +54,4 @@
  * Add more actions.
  * Significant cleanup of rules handling.
  * Better error reporting.
 - 
 \ No newline at end of file
 + 
+diff -urN -x .svn hotplug2-0.9/common.mak hotplug2/common.mak
+--- hotplug2-0.9/common.mak    2006-09-26 01:03:08.000000000 +0200
++++ hotplug2/common.mak        2007-07-09 01:17:14.869504000 +0200
+@@ -1,6 +1,6 @@
+ # vim:set sw=8 nosta:
+-CFLAGS=-Os -DHAVE_RULES -Wall -g
++CFLAGS=-Os -DHAVE_RULES -Wall -g -Wextra
+ LDFLAGS=-g
+ INSTALL=install -c -m 644
+@@ -10,7 +10,7 @@
+ .PHONY: all clean dep install install-recursive clean-recursive \
+       dep-recursive all-recursive
+-MAKEDEP=-gcc $(CFLAGS) -MM $(wildcard *.c *.cc) > .depend
++MAKEDEP=-$(CC) $(CFLAGS) -MM $(wildcard *.c *.cc) > .depend
+ dep: dep-recursive
+       $(MAKEDEP)
+ .depend:
 diff -urN -x .svn hotplug2-0.9/docs/hotplug2.8 hotplug2/docs/hotplug2.8
 --- hotplug2-0.9/docs/hotplug2.8       2006-09-26 09:23:36.000000000 +0200
-+++ hotplug2/docs/hotplug2.8   2007-06-25 10:51:14.540247912 +0200
-@@ -31,6 +31,8 @@
++++ hotplug2/docs/hotplug2.8   2007-06-28 14:50:59.874955160 +0200
+@@ -22,6 +22,8 @@
+ .TP 
+ \fB\-\-dumb\fR, \fB\-\-no\-dumb\fR
+ Run or do not run hotplug2 in dumb mode. Dumb mode means that rules are being ignored, the only action taken is mload modules to all devices whose uevent exports MODALIAS. Only available if compiled with HAVE_RULES.
++\fB\-\-override\fR, \fB\-\-no\-override\fR
++Allows hotplug2 behavior overriding for different rules, using various flags. See hotplug2 rules documentation for details. The default is not to allow overriding, the flags are therefore ignored.
+ .TP 
+ \fB\-\-max\-children <value>\fR
+ Set the value of maximum children hotplug2 may have running simultaneously. Default is 20.
+@@ -31,6 +33,8 @@
  .TP 
  \fB\-\-set\-modprobe\-cmd <cmd>\fR
  Sets the application used to perform modprobe. It only gets used in dumb mode. Default is to autodetect: if '/bin/modprobe' is from module\-init\-tools, use '/sbin/modprobe', otherwise use '/sbin/hotplug2\-modwrap'.
@@ -61,7 +93,7 @@ diff -urN -x .svn hotplug2-0.9/docs/hotplug2.8 hotplug2/docs/hotplug2.8
  \fBSIGUSR1\fR
 diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.rules.doc
 --- hotplug2-0.9/docs/hotplug2.rules.doc       2006-09-26 10:19:46.000000000 +0200
-+++ hotplug2/docs/hotplug2.rules.doc   2007-06-25 10:51:14.537248368 +0200
++++ hotplug2/docs/hotplug2.rules.doc   2007-06-28 14:50:59.872955464 +0200
 @@ -11,12 +11,12 @@
        [... [...]]
  }
@@ -124,7 +156,7 @@ diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.ru
        
   * chown <path> <owner name>
        Change owner of path to owner name.
-@@ -111,12 +110,14 @@
+@@ -111,12 +110,32 @@
        Change group of path to group name.
        
   * chmod <path> <mode>
@@ -138,10 +170,28 @@ diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.ru
 +
 + * printdebug
 +      Prints all variables read from kernel.
++
++ FLAGS
++ -----
++
++Flags are, syntactically, just like actions; their semantical value is different however.
++Instead of doing something, they instead change the general behavior of the processing
++of the given rule.
++
++Note that for flags to work, you also have to invoke it with --override.
++
++Currently, only one flag is implemented:
++
++ * nothrottle
++      Forcibly overrides hotplug2 throttling mechanism. If _all_ rules that match
++      the given kernel event have 'nothrottle' set, hotplug2 will not wait for
++      children count to get under max-children limit. That allows to throttle
++      eg. helper application execution or modprobes, but yet keep node devices
++      fast.
        
   ESCAPING
   --------
-@@ -136,8 +137,9 @@
+@@ -136,8 +155,9 @@
   SAMPLE CONFIG
   -------------
  
@@ -153,7 +203,7 @@ diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.ru
  ---------------------------------------------------------------------------------
  MODALIAS is set {
        exec modprobe -q %MODALIAS% ;
-@@ -146,3 +148,33 @@
+@@ -146,3 +166,33 @@
  SUBSYSTEM == block, DEVPATH is set, MAJOR is set, MINOR is set {
        makedev /dev/%DEVICENAME% 0644
  }
@@ -189,7 +239,7 @@ diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.ru
 +}
 diff -urN -x .svn hotplug2-0.9/docs/Makefile hotplug2/docs/Makefile
 --- hotplug2-0.9/docs/Makefile 2006-09-26 00:27:02.000000000 +0200
-+++ hotplug2/docs/Makefile     2007-06-25 10:51:14.540247912 +0200
++++ hotplug2/docs/Makefile     2007-06-28 14:50:59.875955008 +0200
 @@ -2,12 +2,13 @@
  
  BINS=
@@ -208,7 +258,7 @@ diff -urN -x .svn hotplug2-0.9/docs/Makefile hotplug2/docs/Makefile
  include ../common.mak
 diff -urN -x .svn hotplug2-0.9/examples/Makefile hotplug2/examples/Makefile
 --- hotplug2-0.9/examples/Makefile     2006-09-26 01:03:08.000000000 +0200
-+++ hotplug2/examples/Makefile 2007-06-25 10:51:14.685225872 +0200
++++ hotplug2/examples/Makefile 2007-06-28 14:50:59.991937376 +0200
 @@ -2,19 +2,23 @@
  
  BINS=
@@ -241,18 +291,427 @@ diff -urN -x .svn hotplug2-0.9/examples/Makefile hotplug2/examples/Makefile
  
  
  include ../common.mak
+diff -urN -x .svn hotplug2-0.9/filemap_utils.c hotplug2/filemap_utils.c
+--- hotplug2-0.9/filemap_utils.c       2006-09-25 12:14:12.000000000 +0200
++++ hotplug2/filemap_utils.c   2007-07-09 02:01:10.966249750 +0200
+@@ -16,7 +16,15 @@
+ #include "filemap_utils.h"
+-int map_file(char *filename, struct filemap_t *filemap) {
++/**
++ * Basic open/mmap wrapper to make things simpler.
++ *
++ * @1 Filename of the mmaped file
++ * @2 Pointer to filemap structure
++ *
++ * Returns: 0 if success, 1 otherwise
++ */
++int map_file(const char *filename, struct filemap_t *filemap) {
+       struct stat statbuf;
+       
+       filemap->fd = open(filename, O_RDONLY);
+@@ -40,9 +48,16 @@
+       return 0;
+ }
++/**
++ * Basic close/munmap wrapper.
++ *
++ * @1 Pointer to filemap structure
++ *
++ * Returns: always 0
++ */
+ int unmap_file(struct filemap_t *filemap) {
+-      close(filemap->fd);
+       munmap(filemap->map, filemap->size);
++      close(filemap->fd);
+       
+       return 0;
+ }
+diff -urN -x .svn hotplug2-0.9/filemap_utils.h hotplug2/filemap_utils.h
+--- hotplug2-0.9/filemap_utils.h       2006-09-25 22:24:36.000000000 +0200
++++ hotplug2/filemap_utils.h   2007-07-09 02:01:10.962249500 +0200
+@@ -14,6 +14,6 @@
+       void *map;
+ };
+-int map_file(char *, struct filemap_t *);
++int map_file(const char *, struct filemap_t *);
+ int unmap_file(struct filemap_t *);
+ #endif
 diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
 --- hotplug2-0.9/hotplug2.c    2006-10-08 15:18:23.000000000 +0200
-+++ hotplug2/hotplug2.c        2007-06-25 10:51:14.688225416 +0200
-@@ -391,6 +391,7 @@
++++ hotplug2/hotplug2.c        2007-07-09 02:01:10.962249500 +0200
+@@ -23,7 +23,9 @@
+ #include <linux/netlink.h>
+ #include "mem_utils.h"
++#include "filemap_utils.h"
+ #include "hotplug2.h"
++#include "hotplug2_utils.h"
+ #include "rules.h"
+ #include "childlist.h"
+@@ -32,10 +34,16 @@
+                                       child == NULL && \
+                                       highest_seqnum == get_kernel_seqnum())
++/*
++ * These variables are accessed from throughout the code.
++ *
++ * TODO: Move this into a hotplug2_t-like variable.
++ */
+ event_seqnum_t highest_seqnum = 0;
+ pid_t coldplug_p;
+ int coldplug = 1;
+ int persistent = 0;
++int override = 0;
+ int max_child_c = 20;
+ int dumb = 0;
+ int terminate = 0;
+@@ -45,6 +53,14 @@
+ char *modprobe_command = NULL;
++/**
++ * Release all memory associated with an uevent read from kernel. The given
++ * pointer is no longer valid, as it gets freed as well.
++ *
++ * @1 The event that is to be freed.
++ *
++ * Returns: void
++ */
+ inline void free_hotplug2_event(struct hotplug2_event_t *event) {
+       int i;
+       
+@@ -57,6 +73,13 @@
+       free(event);
+ }
++/**
++ * A trivial function determining the action that the uevent.
++ *
++ * @1 String containing the action name (null-terminated).
++ *
++ * Returns: Macro of the given action
++ */
+ inline int get_hotplug2_event_action(char *action) {
+       if (!strcmp(action, "add"))
+               return ACTION_ADD;
+@@ -67,6 +90,14 @@
+       return ACTION_UNKNOWN;
+ }
++/**
++ * Looks up a value according to the given key.
++ *
++ * @1 A hotplug event structure
++ * @2 Key for lookup
++ *
++ * Returns: The value of the key or NULL if no such key found
++ */
+ char *get_hotplug2_value_by_key(struct hotplug2_event_t *event, char *key) {
+       int i;
+       
+@@ -78,7 +109,16 @@
+       return NULL;
+ }
+-inline int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) {
++/**
++ * Appends a key-value pair described by the second argument to the
++ * hotplug event.
++ *
++ * @1 A hotplug event structure
++ * @1 An item in format "key=value" to be appended
++ *
++ * Returns: 0 if success, -1 if the string is malformed
++ */
++int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) {
+       char *ptr, *tmp;
+       
+       ptr = strchr(item, '=');
+@@ -94,6 +134,8 @@
+       
+       /*
+        * Variables not generated by kernel but demanded nonetheless...
++       *
++       * TODO: Split this to a different function
+        */
+       if (!strcmp(item, "DEVPATH")) {
+               event->env_vars_c++;
+@@ -109,6 +151,15 @@
+       return 0;
+ }
++/**
++ * Duplicates all allocated memory of a source hotplug event
++ * and returns a new hotplug event, an identical copy of the
++ * source event.
++ *
++ * @1 Source hotplug event structure
++ *
++ * Returns: A copy of the source event structure
++ */
+ inline struct hotplug2_event_t *dup_hotplug2_event(struct hotplug2_event_t *src) {
+       struct hotplug2_event_t *dest;
+       int i;
+@@ -129,6 +180,14 @@
+       return dest;
+ }
++/**
++ * Parses a string into a hotplug event structurs.
++ *
++ * @1 The event string (not null terminated)
++ * @2 The size of the event string
++ *
++ * Returns: A new event structure
++ */
+ inline struct hotplug2_event_t *get_hotplug2_event(char *event_str, int size) {
+       char *ptr;
+       struct hotplug2_event_t *event;
+@@ -161,59 +220,15 @@
+       return event;
+ }
+-inline event_seqnum_t get_kernel_seqnum() {
+-      FILE *fp;
+-      
+-      char filename[64];
+-      char seqnum[64];
+-      
+-      strcpy(filename, sysfs_seqnum_path);
+-      
+-      fp = fopen(filename, "r");
+-      if (fp == NULL)
+-              return 0;
+-      
+-      fread(seqnum, 1, 64, fp);
+-      fclose(fp);
+-      
+-      return strtoull(seqnum, NULL, 0);
+-}
+-
+-inline int init_netlink_socket() {
+-      int netlink_socket;
+-      struct sockaddr_nl snl;
+-      int buffersize = 16 * 1024 * 1024;
+-      
+-      memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+-      snl.nl_family = AF_NETLINK;
+-      snl.nl_pid = getpid();
+-      snl.nl_groups = 1;
+-      netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
+-      if (netlink_socket == -1) {
+-              ERROR("opening netlink","Failed socket: %s.", strerror(errno));
+-              return -1;
+-      }
+-      
+-      if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) {
+-              ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
+-              
+-              /* Somewhat safe default. */
+-              buffersize = 106496;
+-              
+-              if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize))) {
+-                      ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
+-              }
+-      }
+-      
+-      if (bind(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
+-              ERROR("opening netlink","Failed bind: %s.", strerror(errno));
+-              close(netlink_socket);
+-              return -1;
+-      }
+-      
+-      return netlink_socket;
+-}
+-
++/**
++ * Evaluates an argument into a true/false value.
++ *
++ * @1 argument
++ * @2 argument flag
++ * @3 pointer to output value
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_bool_opt(char *argv, char *name, int *value) {
+       int rv = -1;
+       
+@@ -238,7 +253,13 @@
+       }
+ }
+-void cleanup(void) {
++/**
++ * Performs a cleanup; closes uevent socket, resets signal
++ * handlers, waits for all the children.
++ *
++ * Returns: void
++ */
++void cleanup() {
+       pid_t p;
+       
+       close(netlink_socket);
+@@ -254,6 +275,13 @@
+       INFO("cleanup", "All children terminated.");
+ }
++/**
++ * Handles all signals.
++ *
++ * @1 Signal identifier
++ *
++ * Returns: void
++ */
+ void sighandler(int sig) {
+       pid_t p;
+       
+@@ -313,6 +341,14 @@
+ }
+ #ifdef HAVE_RULES
++/**
++ * Execute all rules for this particular event.
++ *
++ * @1 Hotplug event structure
++ * @2 Rules structure, containing array of rules
++ *
++ * Returns: void
++ */
+ void perform_action(struct hotplug2_event_t *event, struct rules_t *rules) {
+       int i, rv;
+       
+@@ -324,13 +360,72 @@
+       
+       free_hotplug2_event(event);
+ }
++
++/**
++ * Iterates through all rules, and performs an AND between all flags that
++ * would apply during execution (ie. all rules that have conditions matching
++ * the hotplug event).
++ *
++ * @1 Hotplug event structure
++ * @2 Rules structure, containing array of rules
++ *
++ * Returns: Flags that apply to all matching rules
++ */
++int flags_eval(struct hotplug2_event_t *event, struct rules_t *rules) {
++      int flags = FLAG_ALL;
++      int match = 0;
++      int i, j;
++
++      for (i = 0; i < rules->rules_c; i++) {
++              match = 1;
++
++              for (j = 0; j < rules->rules[i].conditions_c; j++) {
++                      if (rule_condition_eval(event, &rules->rules[i].conditions[j]) != EVAL_MATCH) {
++                              match = 0;
++                              break;
++                      }
++              }
++
++              /*
++               * Logical AND between flags we've got already and
++               * those we're adding.
++               */
++              if (match) {
++                      rule_flags(&rules->rules[i]);
++                      flags &= rules->rules[i].flags;
++              }
++      }
++
++      /*
++       * A little trick; if no rule matched, we return FLAG_ALL
++       * and have it skipped completely.
++       */
++
++      return flags;
++}
++#else
++#define perform_action(event, rules)
+ #endif
++/**
++ * Blindly modprobe the modalias, nothing more.
++ *
++ * @1 Hotplug event structure
++ * @2 Modalias to be loaded
++ *
++ * Returns: void
++ */
+ void perform_dumb_action(struct hotplug2_event_t *event, char *modalias) {
+       free_hotplug2_event(event);
+       execl(modprobe_command, modprobe_command, "-q", modalias, NULL);
+ }
++/**
++ * Attempt to figure out whether our modprobe command can handle modalias.
++ * If not, use our own wrapper.
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_modprobe_command() {
+       pid_t p;
+       int fds[2];
+@@ -381,6 +476,9 @@
+ }
+ int main(int argc, char *argv[]) {
++      /*
++       * TODO, cleanup
++       */
+       static char buffer[UEVENT_BUFFER_SIZE+512];
+       struct hotplug2_event_t *tmpevent;
+       char *modalias, *seqnum;
+@@ -390,28 +488,39 @@
+       int size;
        int rv = 0;
        int i;
++      unsigned int flags;
        char *coldplug_command = NULL;
 +      char *rules_file = HOTPLUG2_RULE_PATH;
        sigset_t block_mask;
        
        struct rules_t *rules = NULL;
-@@ -435,15 +436,31 @@
+-      struct stat statbuf;
+-      void *filemap;
+-      int rule_fd;
++      struct filemap_t filemap;
+       struct options_t bool_options[] = {
+               {"persistent", &persistent},
+               {"coldplug", &coldplug},
+               {"udevtrigger", &coldplug},     /* compatibility */
++              {"override", &override},
+ #ifdef HAVE_RULES
+               {"dumb", &dumb},
+ #endif
+               {NULL, NULL}
+       };
+       
++      /*
++       * We parse all the options...
++       */
+       for (argc--; argc > 0; argc--) {
+               argv++;
++              /*
++               * TODO, cleanup
++               */
+               for (i = 0; bool_options[i].name != NULL; i++) {
+                       if (!get_bool_opt(*argv, bool_options[i].name, bool_options[i].value)) {
++                              /*
++                               * Bool options are --option or --no-options. If we handled
++                               * it, quit iterating.
++                               */
+                               break;
+                       } else {
+                               if (!strcmp(*argv, "--max-children")) {
+@@ -435,52 +544,52 @@
                                                break;
                                        
                                        modprobe_command = *argv;
@@ -280,14 +739,44 @@ diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
 +       * faillback to dumb mode.
 +       */
        if (!dumb) {
-               filemap = MAP_FAILED;
+-              filemap = MAP_FAILED;
 -              rule_fd = open(HOTPLUG2_RULE_PATH, O_RDONLY | O_NOATIME);
-+              rule_fd = open(rules_file, O_RDONLY | O_NOATIME);
-               if (rule_fd == -1) {
+-              if (rule_fd == -1) {
+-                      dumb = 1;
+-                      ERROR("rules parse","Unable to open rules file: %s.", strerror(errno));
+-                      goto end_rules;
+-              }
+-              
+-              if (fstat(rule_fd, &statbuf)) {
+-                      dumb = 1;
+-                      ERROR("rules parse","Unable to stat rules file: %s.", strerror(errno));
+-                      goto end_rules;
+-              }
+-              
+-              filemap = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, rule_fd, 0);
+-              if (filemap == MAP_FAILED) {
++              if (map_file(rules_file, &filemap)) {
++                      ERROR("rules parse","Unable to open/mmap rules file.");
                        dumb = 1;
-                       ERROR("rules parse","Unable to open rules file: %s.", strerror(errno));
-@@ -477,10 +494,12 @@
+-                      ERROR("rules parse","Unable to mmap rules file: %s.", strerror(errno));
+                       goto end_rules;
+               }
                
+-              rules = rules_from_config((char*)filemap);
++              rules = rules_from_config((char*)(filemap.map), NULL);
+               if (rules == NULL) {
+                       ERROR("rules parse","Unable to parse rules file.");
+                       dumb = 1;
+               }
++
++              unmap_file(&filemap);
+               
+ end_rules:    
+-              if (filemap != MAP_FAILED)
+-                      munmap(filemap, statbuf.st_size);
+-              if (rule_fd != -1)
+-                      close(rule_fd);
+-              
                if (dumb == 1)
                        ERROR("rules parse","Parsing rules failed, switching to dumb mode.");
 -      } else if (!modprobe_command)
@@ -302,25 +791,125 @@ diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
        {
                if (get_modprobe_command()) {
                        ERROR("modprobe_command","Unable to autodetect modprobe command.");
-@@ -536,7 +555,7 @@
+@@ -489,7 +598,10 @@
+               DBG("modprobe_command", "Using modprobe: `%s'.", modprobe_command);
+       }
+       
+-      netlink_socket = init_netlink_socket();
++      /*
++       * Open netlink socket to read the uevents
++       */
++      netlink_socket = init_netlink_socket(NETLINK_BIND);
+       
+       if (netlink_socket == -1) {
+               ERROR("netlink init","Unable to open netlink socket.");
+@@ -503,6 +615,9 @@
+       signal(SIGINT, sighandler);
+       signal(SIGCHLD, sighandler);
+       
++      /*
++       * If we desire coldplugging, we initiate it right now.
++       */
+       if (coldplug) {
+               if (coldplug_command == NULL)
+                       coldplug_command = UDEVTRIGGER_COMMAND;
+@@ -523,10 +638,19 @@
+               coldplug_p = FORK_FINISHED;
+       }
+       
++      /*
++       * Main loop reading uevents
++       */
+       while (!terminate) {
++              /*
++               * Read the uevent packet
++               */
+               size = recv(netlink_socket, &buffer, sizeof(buffer), 0);
+               recv_errno = errno;
+       
++              /*
++               * Parse the event into an event structure
++               */
+               tmpevent = get_hotplug2_event(buffer, size);
+               
+               if (tmpevent == NULL) {
+@@ -534,26 +658,61 @@
+                       continue;
+               }
                
++              /*
++               * Look up two important items of the event
++               */
                modalias = get_hotplug2_value_by_key(tmpevent, "MODALIAS");
                seqnum = get_hotplug2_value_by_key(tmpevent, "SEQNUM");
 -              
 +
++              /*
++               * Seqnum is necessary not to end up in a race with the kernel.
++               */
                if (seqnum == NULL) {
                        free_hotplug2_event(tmpevent);
                        ERROR("reading events", "Malformed event read (missing SEQNUM).");
-@@ -547,7 +566,7 @@
+                       continue;
+               }
+               
++              /*
++               * Maintain seqnum continuity
++               */
+               cur_seqnum = strtoull(seqnum, NULL, 0);
                if (cur_seqnum > highest_seqnum)
                        highest_seqnum = cur_seqnum;
                
 -              if (tmpevent->action == ACTION_ADD && (!dumb || modalias != NULL)) {
++              /*
++               * If we are in smart mode, we'll always pass. If we're in dumb mode,
++               * we only pass events that have 'add' action and have modalias set.
++               */
 +              if ((dumb && tmpevent->action == ACTION_ADD && modalias != NULL) || (!dumb)) {
++                      /*
++                       * Pre-evaluation of the flags
++                       */
++                      if (!dumb && override) {
++                              flags = flags_eval(tmpevent, rules);
++
++                              DBG("flags", "flag returned: %8x", flags);
++
++                              if (flags == FLAG_ALL)
++                                      continue;
++                      } else {
++                              flags = FLAG_UNSET;
++                      }
++
                        /* 
                         * We have more children than we want. Wait until SIGCHLD handler reduces
                         * their numbers.
-@@ -568,11 +587,9 @@
++                       *
++                       * Unless, of course, we've specified otherwise and no rules that match
++                       * need throttling.
+                        */
+-                      while (child_c >= max_child_c) {
+-                              usleep(HOTPLUG2_THROTTLE_INTERVAL);
++                      if (!flags & FLAG_NOTHROTTLE) {
++                              /*
++                               * Okay, throttle away!
++                               */
++                              while (child_c >= max_child_c) {
++                                      usleep(HOTPLUG2_THROTTLE_INTERVAL);
++                              }
+                       }
+                       
+                       sigemptyset(&block_mask);
+@@ -562,17 +721,18 @@
+                       p = fork();
+                       switch (p) {
+                               case -1:
+-                                      ERROR("event","fork failed: %s.", strerror(errno));
++                                      ERROR("event", "fork failed: %s.", strerror(errno));
+                                       break;
+                               case 0:
++                                      /*
++                                       * TODO: We do not have to dup here, or do we?
++                                       */
                                        sigprocmask(SIG_UNBLOCK, &block_mask, 0);
                                        signal(SIGCHLD, SIG_DFL);
                                        signal(SIGUSR1, SIG_DFL);
@@ -332,7 +921,7 @@ diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
                                                perform_dumb_action(dup_hotplug2_event(tmpevent), modalias);
                                        exit(0);
                                        break;
-@@ -593,12 +610,10 @@
+@@ -593,12 +753,10 @@
        signal(SIGINT, SIG_DFL);
        signal(SIGCHLD, SIG_DFL);
        
@@ -345,17 +934,587 @@ diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
  
        cleanup();
        
+diff -urN -x .svn hotplug2-0.9/hotplug2-dnode.c hotplug2/hotplug2-dnode.c
+--- hotplug2-0.9/hotplug2-dnode.c      2006-09-26 17:35:35.000000000 +0200
++++ hotplug2/hotplug2-dnode.c  2007-07-09 01:17:14.869504000 +0200
+@@ -27,6 +27,7 @@
+ #include "mem_utils.h"
+ #include "hotplug2.h"
++#include "hotplug2_utils.h"
+ #include "parser_utils.h"
+ #define MODALIAS_MAX_LEN              1024
+@@ -45,59 +46,17 @@
+ #define TEST_INPUT_BIT(i,bm)  (bm[i / BITS_PER_LONG] & (((unsigned long)1) << (i%BITS_PER_LONG)))
+-int init_netlink_socket() {
+-      int netlink_socket;
+-      struct sockaddr_nl snl;
+-      int buffersize = 16 * 1024 * 1024;
+-      
+-      memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+-      snl.nl_family = AF_NETLINK;
+-      snl.nl_pid = getpid();
+-      snl.nl_groups = 1;
+-      netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
+-      if (netlink_socket == -1) {
+-              ERROR("opening netlink","Failed socket: %s.", strerror(errno));
+-              return -1;
+-      }
+-      
+-      if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUFFORCE, &buffersize, sizeof(buffersize))) {
+-              ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
+-              
+-              /* Somewhat safe default. */
+-              buffersize = 106496;
+-              
+-              if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUF, &buffersize, sizeof(buffersize))) {
+-                      ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
+-              }
+-      }
+-      
+-      if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
+-              ERROR("opening netlink","Failed bind: %s.", strerror(errno));
+-              close(netlink_socket);
+-              return -1;
+-      }
+-      
+-      return netlink_socket;
+-}
+-
+-inline event_seqnum_t get_kernel_seqnum() {
+-      FILE *fp;
+-      
+-      char filename[64];
+-      char seqnum[64];
+-      
+-      strcpy(filename, sysfs_seqnum_path);
+-      
+-      fp = fopen(filename, "r");
+-      if (fp == NULL)
+-              return 0;
+-      
+-      fread(seqnum, 1, 64, fp);
+-      fclose(fp);
+-      
+-      return strtoull(seqnum, NULL, 0);
+-}
+-
++/**
++ * Parses a bitmap; output is a list of offsets of bits of a bitmap
++ * of arbitrary size that are set to 1.
++ *
++ * @1 Name of the bitmap parsed
++ * @2 The actual bitmap pointer
++ * @3 Lower boundary of the bitmap
++ * @4 Upper boundary of the bitmap
++ *
++ * Returns: Newly allocated string containing the offsets
++ */
+ char *bitmap_to_bitstring(char name, unsigned long *bm, unsigned int min_bit, unsigned int max_bit)
+ {
+       char *rv;
+@@ -120,6 +79,15 @@
+       return rv;
+ }
++/**
++ * Reverses the bitmap_to_bitstring function. 
++ *
++ * @1 Bitstring to be converted
++ * @2 Output bitmap
++ * @3 Size of the whole bitmap
++ *
++ * Returns: void
++ */
+ void string_to_bitmap(char *input, unsigned long *bitmap, int bm_len) {
+       char *token, *ptr;
+       int i = 0;
+@@ -146,6 +114,14 @@
+       } \
+       bitmap = bitmap_to_bitstring(name, bitmap ## _bits, min, mapkey ## _MAX);
++/**
++ * Creates an input modalias out of preset environmental variables.
++ *
++ * @1 Pointer to where modalias will be created
++ * @2 Maximum size of the modalias
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_input_modalias(char *modalias, int modalias_len) {
+       char *product_env;
+       char *ptr;
+@@ -245,6 +221,14 @@
+ #undef NBITS
+ #undef TEST_INPUT_BIT
++/**
++ * Creates a PCI modalias out of preset environmental variables.
++ *
++ * @1 Pointer to where modalias will be created
++ * @2 Maximum size of the modalias
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_pci_modalias(char *modalias, int modalias_len) {
+       char *class_env, *id_env, *subsys_env;
+       char *ptr;
+@@ -290,6 +274,15 @@
+       return 0;
+ }
++/**
++ * Creates an IEEE1394 (FireWire) modalias out of preset environmental
++ * variables.
++ *
++ * @1 Pointer to where modalias will be created
++ * @2 Maximum size of the modalias
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_ieee1394_modalias(char *modalias, int modalias_len) {
+       char *vendor_env, *model_env;
+       char *specifier_env, *version_env;
+@@ -317,6 +310,14 @@
+       return 0;
+ }
++/**
++ * Creates a serio modalias out of preset environmental variables.
++ *
++ * @1 Pointer to where modalias will be created
++ * @2 Maximum size of the modalias
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_serio_modalias(char *modalias, int modalias_len) {
+       char *serio_type_env, *serio_proto_env;
+       char *serio_id_env, *serio_extra_env;
+@@ -344,6 +345,14 @@
+       return 0;
+ }
++/**
++ * Creates an USB modalias out of preset environmental variables.
++ *
++ * @1 Pointer to where modalias will be created
++ * @2 Maximum size of the modalias
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
+ int get_usb_modalias(char *modalias, int modalias_len) {
+       char *product_env, *type_env, *interface_env;
+       char *ptr;
+@@ -409,6 +418,16 @@
+       return 0;
+ }
++/**
++ * Distributes modalias generating according to the bus name.
++ *
++ * @1 Bus name
++ * @2 Pointer to where modalias will be created
++ * @3 Maximum size of the modalias
++ *
++ * Returns: The return value of the subsystem modalias function, or -1 if
++ * no match.
++ */
+ int get_modalias(char *bus, char *modalias, int modalias_len) {
+       memset(modalias, 0, modalias_len);
+       
+@@ -435,6 +454,16 @@
+       return -1;
+ }
++/**
++ * Turns all environmental variables as set when invoked by /proc/sys/hotplug
++ * into an uevent formatted (thus not null-terminated) string.
++ *
++ * @1 All environmental variables
++ * @2 Bus of the event (as read from argv)
++ * @3 Pointer to size of the returned uevent string
++ *
++ * Returns: Not null terminated uevent string.
++ */
+ inline char *get_uevent_string(char **environ, char *bus, unsigned long *uevent_string_len) {
+       char *uevent_string;
+       char *tmp;
+@@ -516,7 +545,7 @@
+               return 1;
+       }
+       
+-      netlink_socket = init_netlink_socket();
++      netlink_socket = init_netlink_socket(NETLINK_CONNECT);
+       if (netlink_socket == -1) {
+               ERROR("netlink init","Unable to open netlink socket.");
+               goto exit;
+diff -urN -x .svn hotplug2-0.9/hotplug2.h hotplug2/hotplug2.h
+--- hotplug2-0.9/hotplug2.h    2006-10-08 12:21:56.000000000 +0200
++++ hotplug2/hotplug2.h        2007-07-09 01:17:14.865503750 +0200
+@@ -34,7 +34,7 @@
+ #endif
+ #ifndef O_NOATIME
+-#define O_NOATIME                                     01000000
++#define O_NOATIME                     01000000
+ #endif
+ #define ERROR(action, fmt, arg...)    fprintf(stderr, "[%s]: " fmt"\n", action, ##arg);
+@@ -47,7 +47,7 @@
+ #define UEVENT_BUFFER_SIZE            2048
+ #define HOTPLUG2_POLL_INTERVAL                20000
+-#define HOTPLUG2_THROTTLE_INTERVAL            10000
++#define HOTPLUG2_THROTTLE_INTERVAL    10000
+ #define HOTPLUG2_RULE_PATH            "/etc/hotplug2.rules"
+ #define ACTION_ADD                    0
+diff -urN -x .svn hotplug2-0.9/hotplug2_utils.c hotplug2/hotplug2_utils.c
+--- hotplug2-0.9/hotplug2_utils.c      1970-01-01 01:00:00.000000000 +0100
++++ hotplug2/hotplug2_utils.c  2007-07-09 01:17:14.869504000 +0200
+@@ -0,0 +1,96 @@
++#include <stdio.h>
++#include <string.h>
++#include <stdlib.h>
++#include <fcntl.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <errno.h>
++#include <sys/socket.h>
++#include <sys/types.h>
++#include <sys/un.h>
++#include <sys/wait.h>
++#include <linux/types.h>
++#include <linux/netlink.h>
++
++#include "hotplug2_utils.h"
++
++/**
++ * A trivial function that reads kernel seqnum from sysfs.
++ *
++ * Returns: Seqnum as read from sysfs
++ */
++inline event_seqnum_t get_kernel_seqnum() {
++      FILE *fp;
++      
++      char filename[64];
++      char seqnum[64];
++      
++      strcpy(filename, sysfs_seqnum_path);
++      
++      fp = fopen(filename, "r");
++      if (fp == NULL)
++              return 0;
++      
++      fread(seqnum, 1, 64, fp);
++      fclose(fp);
++      
++      return strtoull(seqnum, NULL, 0);
++}
++
++/**
++ * Opens a PF_NETLINK socket into the kernel, to read uevents.
++ *
++ * @1 Specifies type of socket (whether we bind or whether we connect) 
++ *
++ * Returns: Socket fd if succesful, -1 otherwise.
++ */
++inline int init_netlink_socket(int type) {
++      int netlink_socket;
++      struct sockaddr_nl snl;
++      int buffersize = 16 * 1024 * 1024;
++      
++      memset(&snl, 0x00, sizeof(struct sockaddr_nl));
++      snl.nl_family = AF_NETLINK;
++      snl.nl_pid = getpid();
++      snl.nl_groups = 1;
++      netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
++      if (netlink_socket == -1) {
++              ERROR("opening netlink","Failed socket: %s.", strerror(errno));
++              return -1;
++      }
++      
++      /*
++       * We're trying to override buffer size. If we fail, we attempt to set a big buffer and pray.
++       */
++      if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) {
++              ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
++              
++              /* Somewhat safe default. */
++              buffersize = 106496;
++              
++              if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize))) {
++                      ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
++              }
++      }
++      
++      /*
++       * hotplug2-dnode performs connect, while hotplug2 daemon binds
++       */
++      switch (type) {
++              case NETLINK_CONNECT:
++                      if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
++                              ERROR("opening netlink","Failed connect: %s.", strerror(errno));
++                              close(netlink_socket);
++                              return -1;
++                      }
++                      
++              default:
++                      if (bind(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
++                              ERROR("opening netlink","Failed bind: %s.", strerror(errno));
++                              close(netlink_socket);
++                              return -1;
++                      }
++      }
++      
++      return netlink_socket;
++}
+diff -urN -x .svn hotplug2-0.9/hotplug2_utils.h hotplug2/hotplug2_utils.h
+--- hotplug2-0.9/hotplug2_utils.h      1970-01-01 01:00:00.000000000 +0100
++++ hotplug2/hotplug2_utils.h  2007-07-09 01:17:14.869504000 +0200
+@@ -0,0 +1,21 @@
++/*****************************************************************************\
++*  _  _       _          _              ___                                   *
++* | || | ___ | |_  _ __ | | _  _  __ _ |_  )                                  *
++* | __ |/ _ \|  _|| '_ \| || || |/ _` | / /                                   *
++* |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___|                                  *
++*                 |_|            |___/                                        *
++\*****************************************************************************/
++
++#ifndef HOTPLUG2_UTILS_H
++#define HOTPLUG2_UTILS_H 1
++
++#include "hotplug2.h"
++
++#define NETLINK_UNDEFINED     0
++#define NETLINK_CONNECT               1
++#define NETLINK_BIND          2
++
++inline event_seqnum_t get_kernel_seqnum();
++inline int init_netlink_socket(int);
++
++#endif
+diff -urN -x .svn hotplug2-0.9/linux24_compat/hotplug2-coldplug-2.4.c hotplug2/linux24_compat/hotplug2-coldplug-2.4.c
+--- hotplug2-0.9/linux24_compat/hotplug2-coldplug-2.4.c        2006-09-25 22:22:47.000000000 +0200
++++ hotplug2/linux24_compat/hotplug2-coldplug-2.4.c    2007-07-09 01:17:14.793499250 +0200
+@@ -28,59 +28,7 @@
+ #include "../mem_utils.h"
+ #include "../parser_utils.h"
+ #include "../filemap_utils.h"
+-
+-inline int init_netlink_socket() {
+-      int netlink_socket;
+-      struct sockaddr_nl snl;
+-      int buffersize = 16 * 1024 * 1024;
+-      
+-      memset(&snl, 0x00, sizeof(struct sockaddr_nl));
+-      snl.nl_family = AF_NETLINK;
+-      snl.nl_pid = getpid();
+-      snl.nl_groups = 1;
+-      netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 
+-      if (netlink_socket == -1) {
+-              ERROR("opening netlink","Failed socket: %s.", strerror(errno));
+-              return -1;
+-      }
+-      
+-      if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUFFORCE, &buffersize, sizeof(buffersize))) {
+-              ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
+-              
+-              /* Somewhat safe default. */
+-              buffersize = 106496;
+-              
+-              if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUF, &buffersize, sizeof(buffersize))) {
+-                      ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
+-              }
+-      }
+-      
+-      if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
+-              ERROR("opening netlink","Failed bind: %s.", strerror(errno));
+-              close(netlink_socket);
+-              return -1;
+-      }
+-      
+-      return netlink_socket;
+-}
+-
+-inline event_seqnum_t get_kernel_seqnum() {
+-      FILE *fp;
+-      
+-      char filename[64];
+-      char seqnum[64];
+-      
+-      strcpy(filename, sysfs_seqnum_path);
+-      
+-      fp = fopen(filename, "r");
+-      if (fp == NULL)
+-              return 0;
+-      
+-      fread(seqnum, 1, 64, fp);
+-      fclose(fp);
+-      
+-      return strtoull(seqnum, NULL, 0);
+-}
++#include "../hotplug2_utils.h"
+ inline char *get_uevent_string(char **environ, unsigned long *uevent_string_len) {
+       char *uevent_string;
+@@ -413,7 +361,7 @@
+ int main(int argc, char *argv[], char **environ) {
+       int netlink_socket;
+       
+-      netlink_socket = init_netlink_socket();
++      netlink_socket = init_netlink_socket(NETLINK_CONNECT);
+       if (netlink_socket == -1) {
+               ERROR("netlink init","Unable to open netlink socket.");
+               return 1;
 diff -urN -x .svn hotplug2-0.9/linux24_compat/hotplug2-modwrap.c hotplug2/linux24_compat/hotplug2-modwrap.c
 --- hotplug2-0.9/linux24_compat/hotplug2-modwrap.c     2006-09-25 22:23:07.000000000 +0200
-+++ hotplug2/linux24_compat/hotplug2-modwrap.c 2007-06-25 10:51:14.601238640 +0200
-@@ -122,6 +122,12 @@
++++ hotplug2/linux24_compat/hotplug2-modwrap.c 2007-07-09 01:17:14.789499000 +0200
+@@ -30,8 +30,19 @@
+ #include "../parser_utils.h"
+ #include "../filemap_utils.h"
++#define MODULES_PATH          "/lib/modules/"
++#define MODULES_ALIAS         "modules.alias"
++
++/**
++ * A simple fork/exec wrapper
++ *
++ * @1 Complete argv, including app path
++ *
++ * Returns: -1 if error, children return value otherwise
++ */
+ int execute(char **argv) {
+       pid_t p;
++      int status;
+       
+       p = fork();
+       switch (p) {
+@@ -42,10 +53,11 @@
+                       exit(1);
+                       break;
+               default:
+-                      waitpid(p, NULL, 0);
++                      waitpid(p, &status, 0);
+                       break;
+       }
+-      return 0;
++
++      return WEXITSTATUS(status);
+ }
+ int main(int argc, char *argv[]) {
+@@ -63,21 +75,36 @@
+       
+       match_alias = strdup(argv[argc - 1]);
+       
++      /*
++       * If we can't do uname, we're absolutely screwed and there's no
++       * sense thinking twice about anything.
++       */
+       if (uname(&unamebuf)) {
+               ERROR("uname", "Unable to perform uname: %s.", strerror(errno));
+               return 1;
+       }
+       
+-      /* We use this one */
++      /*
++       * We allow setting the modprobe command to an arbitrary value.
++       *
++       * The whole trick lies in executing modprobe with exactly the
++       * same argv as this app was executed, except we use a different
++       * argv[0] (application path) and argv[argc-1] (we substitute
++       * the given modalias by the matching module name)
++       */
+       argv[0] = getenv("MODPROBE_COMMAND");
+       if (argv[0] == NULL)
+               argv[0] = "/sbin/modprobe";
+-      
+-      /* "/lib/modules/" + "/" + "\0" */
+-      filename = xmalloc(15 + strlen(unamebuf.release) + strlen("modules.alias"));
+-      strcpy(filename, "/lib/modules/");
++
++      /*
++       * Compose a path, /lib/modules/`uname -r`/modules.alias
++       *
++       * "/lib/modules/" + "/" + "\0" 
++       */
++      filename = xmalloc(strlen(MODULES_PATH) + strlen(unamebuf.release) + strlen(MODULES_ALIAS));
++      strcpy(filename, MODULES_PATH);
+       strcat(filename, unamebuf.release);
+-      strcat(filename, "/modules.alias");
++      strcat(filename, MODULES_ALIAS);
+       
+       if (map_file(filename, &aliasmap)) {
+               ERROR("map_file", "Unable to map file: `%s'.", filename);
+@@ -86,10 +113,16 @@
+               return 1;
+       }
+       
++      /*
++       * Read all the aliases, match them against given parameter.
++       */
+       nptr = aliasmap.map;
+       while ((line = dup_line(nptr, &nptr)) != NULL) {
+               nline = line;
+               
++              /*
++               * We want aliases only
++               */
+               token = dup_token(nline, &nline, isspace);
+               if (!token || strcmp(token, "alias")) {
+                       free(token);
+@@ -98,12 +131,18 @@
+               }
+               free(token);
+               
++              /*
++               * It's an alias, so fetch it
++               */
+               cur_alias = dup_token(nline, &nline, isspace);
+               if (!cur_alias) {
+                       free(line);
+                       continue;
+               }
+               
++              /*
++               * And now we get the module name
++               */
+               module = dup_token(nline, &nline, isspace);
+               if (!module) {
+                       free(line);
+@@ -111,10 +150,14 @@
+                       continue;
+               }
+               
++              /*
++               * If we match, we do the modalias->module name
++               * substitution as described above and execute.
++               */
+               if (!fnmatch(cur_alias, match_alias, 0)) {
+                       argv[argc - 1] = module;
+                       if (execute(argv)) {
+-                              ERROR("execute", "Unable to execute: `%s'.", argv[0]);
++                              ERROR("execute", "Error during exection of: `%s'.", argv[0]);
+                       }
+               }
+               
+@@ -122,6 +165,17 @@
                free(module);
                free(line);
        }
 +
++      /*
++       * Perhaps we didn't match anything, so we might've been given
++       * a module name instead of a modalias. Try to modprobe it
++       * right away.
++       */
 +      if (strcmp(argv[argc - 1], match_alias) == 0) {
 +              if (execute(argv)) {
-+                      ERROR("execute", "Unable to execute: `%s'.", argv[0]);
++                      ERROR("execute", "Error during exection of: `%s'.", argv[0]);
 +              }
 +      }       
        
@@ -363,8 +1522,8 @@ diff -urN -x .svn hotplug2-0.9/linux24_compat/hotplug2-modwrap.c hotplug2/linux2
        free(match_alias);
 diff -urN -x .svn hotplug2-0.9/linux24_compat/Makefile hotplug2/linux24_compat/Makefile
 --- hotplug2-0.9/linux24_compat/Makefile       2006-09-26 00:26:46.000000000 +0200
-+++ hotplug2/linux24_compat/Makefile   2007-06-25 10:51:14.601238640 +0200
-@@ -2,13 +2,14 @@
++++ hotplug2/linux24_compat/Makefile   2007-07-09 01:17:14.793499250 +0200
+@@ -2,16 +2,17 @@
  
  BINS=generate_alias hotplug2-coldplug-2.4 hotplug2-modwrap
  SUBDIRS=
@@ -380,11 +1539,15 @@ diff -urN -x .svn hotplug2-0.9/linux24_compat/Makefile hotplug2/linux24_compat/M
 +      $(INSTALL_BIN) generate_alias $(DESTDIR)/usr/sbin/
  
  
- hotplug2-coldplug-2.4: hotplug2-coldplug-2.4.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
+-hotplug2-coldplug-2.4: hotplug2-coldplug-2.4.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
++hotplug2-coldplug-2.4: hotplug2-coldplug-2.4.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o ../hotplug2_utils.o
+ hotplug2-modwrap: hotplug2-modwrap.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
+ generate_alias: generate_alias.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
 diff -urN -x .svn hotplug2-0.9/Makefile hotplug2/Makefile
 --- hotplug2-0.9/Makefile      2006-09-26 01:03:08.000000000 +0200
-+++ hotplug2/Makefile  2007-06-25 10:51:14.693224656 +0200
-@@ -2,12 +2,13 @@
++++ hotplug2/Makefile  2007-07-09 01:17:14.869504000 +0200
+@@ -2,16 +2,17 @@
  
  BINS=hotplug2 hotplug2-dnode
  SUBDIRS=linux24_compat docs examples
@@ -398,14 +1561,132 @@ diff -urN -x .svn hotplug2-0.9/Makefile hotplug2/Makefile
 +      $(INSTALL_BIN) $(BINS) $(DESTDIR)/sbin/
  
  
- hotplug2: hotplug2.o childlist.o mem_utils.o rules.o
+-hotplug2: hotplug2.o childlist.o mem_utils.o rules.o
+-hotplug2-dnode: hotplug2-dnode.o mem_utils.o parser_utils.o
++hotplug2: hotplug2.o hotplug2_utils.o childlist.o mem_utils.o rules.o filemap_utils.o
++hotplug2-dnode: hotplug2-dnode.o hotplug2_utils.o mem_utils.o parser_utils.o
+ include common.mak
+diff -urN -x .svn hotplug2-0.9/mem_utils.c hotplug2/mem_utils.c
+--- hotplug2-0.9/mem_utils.c   2006-09-25 22:21:45.000000000 +0200
++++ hotplug2/mem_utils.c       2007-07-09 01:17:14.865503750 +0200
+@@ -9,6 +9,13 @@
+ #include <stdlib.h>
+ #include <stdio.h>
++/**
++ * A malloc wrapper. Exits if no memory.
++ *
++ * @1 Ammount of memory to allocate
++ *
++ * Returns: Pointer to freshly allocated memory
++ */
+ inline void *xmalloc(size_t size) {
+       void *ptr;
+       ptr = malloc(size);
+@@ -19,6 +26,14 @@
+       return ptr;
+ }
++/**
++ * A realloc wrapper. Exits if no memory.
++ *
++ * @1 Old pointer
++ * @2 Ammount of memory to allocate
++ *
++ * Returns: Pointer to reallocated memory
++ */
+ inline void *xrealloc(void *inptr, size_t size) {
+       void *ptr;
+       ptr = realloc(inptr, size);
+diff -urN -x .svn hotplug2-0.9/parser_utils.c hotplug2/parser_utils.c
+--- hotplug2-0.9/parser_utils.c        2006-09-25 22:21:13.000000000 +0200
++++ hotplug2/parser_utils.c    2007-07-09 01:17:14.865503750 +0200
+@@ -12,6 +12,16 @@
+ #include "mem_utils.h"
+ #include "parser_utils.h"
++/**
++ * Creates a newly allocated null-terminated string representing line
++ * starting at a given pointer and ending at the closest newline. If
++ * no newline present, returns NULL. TODO, use dup_token
++ *
++ * @1 Starting pointer
++ * @2 Pointer where the end position is returned
++ *
++ * Returns: Newly allocated string containing the line or NULL
++ */
+ char *dup_line(char *start, char **nptr) {
+       char *ptr, *rv;
+       
+@@ -29,6 +39,15 @@
+       return rv;
+ }
++/**
++ * Returns a token delimited by the given function.
++ *
++ * @1 Starting pointer
++ * @2 Pointer where the end position is returned
++ * @3 Function that identifies the delimiter characters
++ *
++ * Returns: Newly allocated string containing the token or NULL
++ */
+ char *dup_token(char *start, char **nptr, int (*isdelimiter)(int)) {
+       char *ptr, *rv;
+       
+@@ -56,6 +75,16 @@
+       return rv;
+ }
++/**
++ * Returns the last token delimited by the given function.
++ *
++ * @1 Starting pointer of the whole string
++ * @2 Starting position
++ * @3 Pointer where the end position is returned
++ * @4 Function that identifies the delimiter characters
++ *
++ * Returns: Newly allocated string containing the token or NULL
++ */
+ char *dup_token_r(char *start, char *start_string, char **nptr, int (*isdelimiter)(int)) {
+       char *ptr, *rv;
+       
 diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
 --- hotplug2-0.9/rules.c       2006-09-29 22:19:31.000000000 +0200
-+++ hotplug2/rules.c   2007-06-25 10:51:14.692224808 +0200
-@@ -59,6 +59,24 @@
++++ hotplug2/rules.c   2007-07-09 02:01:10.962249500 +0200
+@@ -22,11 +22,18 @@
+ #include <sys/stat.h>
+ #include "mem_utils.h"
++#include "filemap_utils.h"
+ #include "hotplug2.h"
+ #include "rules.h"
+-#define last_rule return_rules->rules[return_rules->rules_c - 1]
++/**
++ * Function supplementing 'mkdir -p'.
++ *
++ * @1 Path to be mkdir'd
++ *
++ * Returns: void
++ */
+ static void mkdir_p(char *path) {
+       char *ptr;
+       struct stat statbuf;
+@@ -59,6 +66,40 @@
        free(path);
  }
  
++/**
++ * Function supplementing 'rmdir -p'.
++ *
++ * @1 Path to be rmdir'd
++ *
++ * Returns: void
++ */
 +static void rmdir_p(char *path) {
 +      char *ptr;
 +      
@@ -424,14 +1705,156 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
 +      free(path);
 +}
 +
++/**
++ * Replaces all needles by a given value.
++ *
++ * @1 Haystack (which gets free'd in the function)
++ * @2 Needle
++ * @3 Needle replacement
++ *
++ * Returns: Newly allocated haysteck after replacement.
++ */
  static char *replace_str(char *hay, char *needle, char *replacement) {
          char *ptr, *start, *bptr, *buf;
          int occurences, j;
-@@ -250,11 +268,30 @@
+@@ -128,7 +169,15 @@
+         return buf;
+ }
+-inline int isescaped(char *hay, char *ptr) {
++/**
++ * Trivial utility, figuring out whether a character is escaped or not.
++ *
++ * @1 Haystack
++ * @2 Pointer to the character in question
++ *
++ * Returns: 1 if escaped, 0 otherwise
++ */
++static inline int isescaped(char *hay, char *ptr) {
+       if (ptr <= hay)
+               return 0;
+       
+@@ -138,6 +187,15 @@
+       return 1;
+ }
++/**
++ * Performs replacement of all keys by their value based on the hotplug
++ * event structure. Keys are identified as strings %KEY%.
++ *
++ * @1 Haystack
++ * @2 Hotplug event structure
++ *
++ * Returns: Newly allocated haystack (old is freed)
++ */
+ static char *replace_key_by_value(char *hay, struct hotplug2_event_t *event) {
+       char *sptr = hay, *ptr = hay;
+       char *buf, *replacement;
+@@ -171,6 +229,17 @@
+       return hay;
+ }
++/**
++ * Obtains all information from hotplug event structure about a device node.
++ * Creates the device node at a given path (expandable by keys) and with
++ * given mode.
++ *
++ * @1 Hotplug event structure
++ * @2 Path (may contain keys)
++ * @3 Mode of the file
++ *
++ * Returns: 0 if success, non-zero otherwise
++ */
+ static int make_dev_from_event(struct hotplug2_event_t *event, char *path, mode_t devmode) {
+       char *subsystem, *major, *minor, *devpath;
+       int rv = 1;
+@@ -196,12 +265,27 @@
+       path = replace_key_by_value(path, event);
+       mkdir_p(path);
+       rv = mknod(path, devmode, makedev(atoi(major), atoi(minor)));
++
++      /*
++       * Fixes an issue caused by devmode being modified by umask.
++       */
++      chmod(path, devmode);
++
+       free(path);
+       
+ return_value:
+       return rv;
+ }
++/**
++ * Execute an application without invoking a shell.
++ *
++ * @1 Hotplug event structure
++ * @2 Path to the application, with expandable keys
++ * @3 Argv for the application, with expandable keys
++ *
++ * Returns: Exit status of the application.
++ */
+ static int exec_noshell(struct hotplug2_event_t *event, char *application, char **argv) {
+       pid_t p;
+       int i, status;
+@@ -211,11 +295,12 @@
+               case -1:
+                       return -1;
+               case 0:
++                      application = replace_key_by_value(strdup(application), event);
+                       for (i = 0; argv[i] != NULL; i++) {
+                               argv[i] = replace_key_by_value(argv[i], event);
+                       }
+                       execvp(application, argv);
+-                      exit(0);
++                      exit(127);
+                       break;
+               default:
+                       if (waitpid(p, &status, 0) == -1)
+@@ -226,6 +311,14 @@
+       }
+ }
++/**
++ * Execute an application while invoking a shell.
++ *
++ * @1 Hotplug event structure
++ * @2 The application and all its arguments, with expandable keys
++ *
++ * Returns: Exit status of the application.
++ */
+ static int exec_shell(struct hotplug2_event_t *event, char *application) {
+       int rv;
+       
+@@ -235,6 +328,15 @@
+       return rv;
+ }
++/**
++ * Create a symlink, with necessary parent directories.
++ *
++ * @1 Hotplug event structure
++ * @2 Link target, with expandable keys
++ * @3 Link name, with expandable keys
++ *
++ * Returns: return value of symlink()
++ */
+ static int make_symlink(struct hotplug2_event_t *event, char *target, char *linkname) {
+       int rv;
+       
+@@ -250,11 +352,50 @@
        return rv;
  }
  
 -static int chown_chgrp(int action, char *file, char *param) {
++/**
++ * Chmod a given file.
++ *
++ * @1 Hotplug event structure
++ * @2 File name, with expandable keys
++ * @3 Chmod value, with expandable keys
++ *
++ * Returns: return value of chmod()
++ */
 +static int chmod_file(struct hotplug2_event_t *event, char *file, char *value) {
 +      int rv;
 +
@@ -446,6 +1869,17 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
 +      return rv;
 +}
 +
++
++/**
++ * Change owner or group of a given file.
++ *
++ * @1 Hotplug event structure
++ * @2 Whether we chown or chgrp
++ * @3 Filename, with expandable keys
++ * @4 Group or user name, with expandable keys
++ *
++ * Returns: return value of chown()
++ */
 +static int chown_chgrp(struct hotplug2_event_t *event, int action, char *file, char *param) {
        struct group *grp;
        struct passwd *pwd;
@@ -460,7 +1894,7 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
        switch (action) {
                case ACT_CHOWN:
                        pwd = getpwnam(param);
-@@ -265,8 +302,20 @@
+@@ -265,11 +406,37 @@
                        rv = chown(file, -1, grp->gr_gid);
                        break;
        }
@@ -472,17 +1906,52 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
 +      return rv;
 +}
 +
-+static int print_debug(struct hotplug2_event_t *event) {
++/**
++ * Prints all uevent keys.
++ *
++ * @1 Hotplug event structure
++ *
++ * Returns: void
++ */
++static void print_debug(struct hotplug2_event_t *event) {
 +      int i;
 +
 +      for (i = 0; i < event->env_vars_c; i++)
 +              printf("%s=%s\n", event->env_vars[i].key, event->env_vars[i].value);
-+
-+      return 0;
  }
  
- static int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
-@@ -347,11 +396,11 @@
+-static int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
++/**
++ * Evaluates a condition according to a given hotplug event structure.
++ *
++ * @1 Hotplug event structure
++ * @2 Condition to be evaluated
++ *
++ * Returns: 1 if match, 0 if no match, EVAL_NOT_AVAILABLE if unable to
++ * perform evaluation
++ */
++int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
+       int rv;
+       char *event_value = NULL;
+       regex_t preg;
+@@ -314,6 +481,16 @@
+       return EVAL_NOT_AVAILABLE;
+ }
++/**
++ * Executes a rule. Contains evaluation of all conditions prior
++ * to execution.
++ *
++ * @1 Hotplug event structure
++ * @2 The rule to be executed
++ *
++ * Returns: 0 if success, -1 if the whole event is to be 
++ * discared, 1 if bail out of this particular rule was required
++ */
+ int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) {
+       int i, last_rv;
+       
+@@ -347,11 +524,11 @@
                                last_rv = make_dev_from_event(event, rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0));
                                break;
                        case ACT_CHMOD:
@@ -496,7 +1965,7 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
                                break;
                        case ACT_SYMLINK:
                                last_rv = make_symlink(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]);
-@@ -365,6 +414,13 @@
+@@ -365,12 +542,49 @@
                        case ACT_SETENV:
                                last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1);
                                break;
@@ -505,24 +1974,243 @@ diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
 +                              rmdir_p(rule->actions[i].parameter[0]);
 +                              break;
 +                      case ACT_DEBUG:
-+                              last_rv = print_debug(event);
++                              print_debug(event);
++                              last_rv = 0;
 +                              break;
                }
        }
        
-@@ -518,6 +574,8 @@
+       return 0;
+ }
++/**
++ * Sets the flags of the given rule.
++ *
++ * @1 Rule structure
++ *
++ * Returns: void
++ */
++void rule_flags(struct rule_t *rule) {
++      int i;
++
++      for (i = 0; i < rule->actions_c; i++) {
++              switch (rule->actions[i].type) {
++                      case ACT_FLAG_NOTHROTTLE:
++                              rule->flags |= FLAG_NOTHROTTLE;
++                              break;
++              }
++      }
++      
++      return;
++}
++
++/**
++ * Checks whether the given character should initiate
++ * further parsing.
++ *
++ * @1 Character to examine
++ *
++ * Returns: 1 if it should, 0 otherwise
++ */
+ static inline int isinitiator(int c) {
+       switch (c) {
+               case ',':
+@@ -383,6 +597,16 @@
+       return 0;
+ }
++/**
++ * Appends a character to a buffer. Enlarges if necessary.
++ *
++ * @1 Pointer to the buffer
++ * @2 Pointer to buffer size
++ * @3 Pointer to last buffer character
++ * @4 Appended character
++ *
++ * Returns: void
++ */
+ static inline void add_buffer(char **buf, int *blen, int *slen, char c) {
+       if (*slen + 1 >= *blen) {
+               *blen = *blen + 64;
+@@ -394,6 +618,14 @@
+       *slen += 1;
+ }
++/**
++ * Parses a string into a syntactically acceptable value.
++ *
++ * @1 Input string
++ * @2 Pointer to the new position
++ *
++ * Returns: Newly allocated string.
++ */
+ static char *rules_get_value(char *input, char **nptr) {
+       int quotes = QUOTES_NONE;
+       char *ptr = input;
+@@ -471,6 +703,16 @@
+       return buf;
+ }
++/**
++ * Releases all memory associated with the ruleset. TODO: Make
++ * the behavior same for all _free() functions, ie. either
++ * release the given pointer itself or keep it, but do it
++ * in all functions!
++ *
++ * @1 The ruleset to be freed
++ *
++ * Returns: void
++ */
+ void rules_free(struct rules_t *rules) {
+       int i, j, k;
+       
+@@ -492,10 +734,53 @@
+       free(rules->rules);
+ }
+-struct rules_t *rules_from_config(char *input) {
+-      int status = STATUS_KEY, terminate;
++/**
++ * Includes a rule file.
++ *
++ * @1 Filename
++ * @2 The ruleset structure
++ *
++ * Returns: 0 if success, -1 otherwise
++ */
++int rules_include(const char *filename, struct rules_t **return_rules) {
++      struct filemap_t filemap;
++      struct rules_t *rules;
++
++      if (map_file(filename, &filemap)) {
++              ERROR("rules parse","Unable to open/mmap rules file.");
++              return -1;
++      }
++      
++      rules = rules_from_config((char*)(filemap.map), *return_rules);
++      if (rules == NULL) {
++              ERROR("rules parse","Unable to parse rules file.");
++              return -1;
++      }
++
++      unmap_file(&filemap);
++
++      return 0;
++}
++
++/**
++ * Parses an entire file of rules.
++ *
++ * @1 The whole file in memory or mmap'd
++ *
++ * Returns: A newly allocated ruleset.
++ */
++struct rules_t *rules_from_config(char *input, struct rules_t *return_rules) {
++      #define last_rule return_rules->rules[return_rules->rules_c - 1]
++      int nested;
++      int status;     
++      int terminate;
+       char *buf;
+-      struct rules_t *return_rules;
++
++      /*
++       * TODO: cleanup
++       *
++       * BIIIG cleanup... Use callbacks for actions and for internal actions.
++       */
+       
+       int i, j;
+       struct key_rec_t conditions[] = {       /*NOTE: We never have parameters for conditions. */
+@@ -506,6 +791,7 @@
+               {"!~", 0, COND_NMATCH_RE},
+               {NULL, 0, -1}
+       };
++
+       struct key_rec_t actions[] = {
+               /*one line / one command*/
+               {"run", 1, ACT_RUN_SHELL},
+@@ -518,6 +804,9 @@
                {"chmod", 2, ACT_CHMOD},
                {"chgrp", 2, ACT_CHGRP},
                {"setenv", 2, ACT_SETENV},
 +              {"remove", 1, ACT_REMOVE},
++              {"nothrottle", 0, ACT_FLAG_NOTHROTTLE},
 +              {"printdebug", 0, ACT_DEBUG},
                /*symlink*/
                {"symlink", 2, ACT_SYMLINK},
                {"softlink", 2, ACT_SYMLINK},
+@@ -527,9 +816,19 @@
+               {NULL, 0, -1}
+       };
+-      return_rules = xmalloc(sizeof(struct rules_t));
+-      return_rules->rules_c = 1;
+-      return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c);
++      /*
++       * A little trick for inclusion.
++       */
++      if (return_rules == NULL) {
++              return_rules = xmalloc(sizeof(struct rules_t));
++              return_rules->rules_c = 1;
++              return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c);
++              nested = 0;
++      } else {
++              nested = 1;
++      }
++
++      status = STATUS_KEY;
+       
+       last_rule.actions = NULL;
+       last_rule.actions_c = 0;
+@@ -549,9 +848,26 @@
+                       /* Skip to next line */
+                       while (*input != '\0' && *input != '\n')
+                               input++;
++
++                      free(buf);
++                      continue;
++              } else if (buf[0] == '$') {
++                      buf++;
++
++                      /*
++                       * Warning, hack ahead...
++                       */
++                      if (!strcmp("include", buf)) {
++                              buf = rules_get_value(input, &input);
++                              if (rules_include(buf, &return_rules)) {
++                                      ERROR("rules_include", "Unable to include ruleset '%s'!", buf);
++                              }
++                      }
++
++                      free(buf);
+                       continue;
+               }
+-              
++
+               switch (status) {
+                       case STATUS_KEY:
+                               last_rule.conditions_c++;
+@@ -684,8 +1000,14 @@
+               return_rules->rules_c--;
+               return return_rules;
+       } else {
+-              rules_free(return_rules);
+-              free(return_rules);
++              /*
++               * We don't want to cleanup if we're nested.
++               */
++              if (!nested) {
++                      rules_free(return_rules);
++                      free(return_rules);
++              }
++
+               return NULL;
+       }
+ }
 diff -urN -x .svn hotplug2-0.9/rules.h hotplug2/rules.h
 --- hotplug2-0.9/rules.h       2006-09-25 13:42:22.000000000 +0200
-+++ hotplug2/rules.h   2007-06-25 10:51:14.687225568 +0200
-@@ -24,9 +24,11 @@
++++ hotplug2/rules.h   2007-07-09 02:01:10.962249500 +0200
+@@ -24,9 +24,12 @@
  #define ACT_CHGRP                     6       /* chgrp <...> */
  #define ACT_CHOWN                     7       /* chown <...> */
  #define ACT_SYMLINK                   8       /* symlink <...> */
@@ -532,11 +2220,45 @@ diff -urN -x .svn hotplug2-0.9/rules.h hotplug2/rules.h
  #define ACT_SETENV                    11      /* setenv <...> */
 +#define ACT_REMOVE                    12      /* remove <...> */
 +#define ACT_DEBUG                     13      /* debug */
++#define ACT_FLAG_NOTHROTTLE           14      /* sets 'nothrottle' flag */
  
  #define EVAL_MATCH                    1
  #define EVAL_NOT_MATCH                        0
+@@ -42,6 +45,10 @@
+ #define STATUS_INITIATOR              3       /* ',' for next cond, '{' for block*/
+ #define STATUS_ACTION                 4       /* viz ACT_* and '}' for end of block */
++#define FLAG_UNSET                    0
++#define FLAG_ALL                      0xffffffff
++#define FLAG_NOTHROTTLE                       1       /* We want this rule to ignore max_children limit */
++
+ struct key_rec_t {
+       char *key;
+       int param;
+@@ -65,6 +72,8 @@
+       
+       struct action_t *actions;
+       int actions_c;
++
++      unsigned int flags;
+ };
+ struct rules_t {
+@@ -72,8 +81,10 @@
+       int rules_c;
+ };
++int rule_condition_eval(struct hotplug2_event_t *, struct condition_t *);
+ int rule_execute(struct hotplug2_event_t *, struct rule_t *);
++void rule_flags(struct rule_t *);
+ void rules_free(struct rules_t *);
+-struct rules_t *rules_from_config(char *);
++struct rules_t *rules_from_config(char *, struct rules_t *);
+ #endif /* ifndef RULES_H*/
+Binary files hotplug2-0.9/.swp and hotplug2/.swp differ
 diff -urN -x .svn hotplug2-0.9/TODO hotplug2/TODO
 --- hotplug2-0.9/TODO  1970-01-01 01:00:00.000000000 +0100
-+++ hotplug2/TODO      2007-06-25 10:51:14.691224960 +0200
++++ hotplug2/TODO      2007-06-28 14:51:00.012934184 +0200
 @@ -0,0 +1 @@
 + - live rules update (via inotify)
This page took 0.067046 seconds and 4 git commands to generate.