hotplug2 update - include hotplug2-init.rules from hotplug2.rules, fix permissions...
[openwrt/openwrt.git] / package / hotplug2 / patches / 100-svn_update.patch
index 2844c6e..283e613 100644 (file)
@@ -1,4 +1,4 @@
-diff -urN -x.svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
+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-30 12:59:20.459674000 +0200
 @@ -1,7 +1,11 @@
@@ -26,30 +26,41 @@ diff -urN -x.svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
 -...anyone taking more than a short peek at the software.
 \ No newline at end of file
 +...anyone taking more than a short peek at the software.
-diff -urN -x.svn hotplug2-0.9/Changelog hotplug2/Changelog
+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-28 14:51:00.009934640 +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
+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-06-28 14:54:56.013056712 +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
@@ -59,7 +70,7 @@ diff -urN -x.svn hotplug2-0.9/common.mak hotplug2/common.mak
  dep: dep-recursive
        $(MAKEDEP)
  .depend:
-diff -urN -x.svn hotplug2-0.9/docs/hotplug2.8 hotplug2/docs/hotplug2.8
+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-28 14:50:59.874955160 +0200
 @@ -22,6 +22,8 @@
@@ -80,7 +91,7 @@ diff -urN -x.svn hotplug2-0.9/docs/hotplug2.8 hotplug2/docs/hotplug2.8
  .SH "SIGNALS"
  .TP 
  \fBSIGUSR1\fR
-diff -urN -x.svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.rules.doc
+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-28 14:50:59.872955464 +0200
 @@ -11,12 +11,12 @@
@@ -226,7 +237,7 @@ diff -urN -x.svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.rul
 +ACTION == remove, PHYSDEVPATH ~~ "/usb[0-9]*/", DEVICENAME ~~ "^sd[a-z][0-9]+$", MAJOR is set, MINOR is set {
 +      exec umount /mnt/%DEVICENAME%
 +}
-diff -urN -x.svn hotplug2-0.9/docs/Makefile hotplug2/docs/Makefile
+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-28 14:50:59.875955008 +0200
 @@ -2,12 +2,13 @@
@@ -245,7 +256,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
+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-28 14:50:59.991937376 +0200
 @@ -2,19 +2,23 @@
@@ -280,10 +291,78 @@ diff -urN -x.svn hotplug2-0.9/examples/Makefile hotplug2/examples/Makefile
  
  
  include ../common.mak
-diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
+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-30 12:59:20.459674000 +0200
-@@ -36,6 +36,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;
@@ -291,11 +370,236 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
  int max_child_c = 20;
  int dumb = 0;
  int terminate = 0;
-@@ -324,6 +325,41 @@
+@@ -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;
@@ -316,7 +620,7 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
 +               * those we're adding.
 +               */
 +              if (match) {
-+                      rule_flags(event, &rules->rules[i]);
++                      rule_flags(&rules->rules[i]);
 +                      flags &= rules->rules[i].flags;
 +              }
 +      }
@@ -332,18 +636,54 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
 +#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) {
-@@ -390,7 +426,9 @@
+       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;
-+      int flags;
++      unsigned int flags;
        char *coldplug_command = NULL;
 +      char *rules_file = HOTPLUG2_RULE_PATH;
        sigset_t block_mask;
        
        struct rules_t *rules = NULL;
-@@ -402,6 +440,7 @@
+-      struct stat statbuf;
+-      void *filemap;
+-      int rule_fd;
++      struct filemap_t filemap;
+       struct options_t bool_options[] = {
                {"persistent", &persistent},
                {"coldplug", &coldplug},
                {"udevtrigger", &coldplug},     /* compatibility */
@@ -351,7 +691,27 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
  #ifdef HAVE_RULES
                {"dumb", &dumb},
  #endif
-@@ -435,15 +474,31 @@
+               {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;
@@ -379,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 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;
-                       ERROR("rules parse","Unable to open rules file: %s.", strerror(errno));
-@@ -477,10 +532,12 @@
+               }
++
++              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)
@@ -401,23 +791,83 @@ 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 +593,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,13 +604,35 @@
+                       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
++                       * Pre-evaluation of the flags
 +                       */
 +                      if (!dumb && override) {
 +                              flags = flags_eval(tmpevent, rules);
@@ -449,7 +899,7 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
                        }
                        
                        sigemptyset(&block_mask);
-@@ -562,17 +641,15 @@
+@@ -562,17 +721,18 @@
                        p = fork();
                        switch (p) {
                                case -1:
@@ -457,6 +907,9 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
 +                                      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);
@@ -468,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 +670,10 @@
+@@ -593,12 +753,10 @@
        signal(SIGINT, SIG_DFL);
        signal(SIGCHLD, SIG_DFL);
        
@@ -481,26 +934,596 @@ diff -urN -x.svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
  
        cleanup();
        
-diff -urN -x.svn hotplug2-0.9/linux24_compat/hotplug2-modwrap.c hotplug2/linux24_compat/hotplug2-modwrap.c
+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-28 14:50:59.926947256 +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]);
 +              }
 +      }       
        
        free(filename);
        free(match_alias);
-diff -urN -x.svn hotplug2-0.9/linux24_compat/Makefile hotplug2/linux24_compat/Makefile
+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-28 14:50:59.926947256 +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=
@@ -516,11 +1539,15 @@ diff -urN -x.svn hotplug2-0.9/linux24_compat/Makefile hotplug2/linux24_compat/Ma
 +      $(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
-diff -urN -x.svn hotplug2-0.9/Makefile hotplug2/Makefile
+-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-28 14:51:00.014933880 +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
@@ -534,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
-diff -urN -x.svn hotplug2-0.9/rules.c hotplug2/rules.c
+-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-30 12:44:52.501430000 +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;
 +      
@@ -560,23 +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;
-@@ -128,7 +146,7 @@
+@@ -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;
        
-@@ -250,11 +268,30 @@
+@@ -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;
 +
@@ -591,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;
@@ -605,7 +1894,7 @@ diff -urN -x.svn hotplug2-0.9/rules.c hotplug2/rules.c
        switch (action) {
                case ACT_CHOWN:
                        pwd = getpwnam(param);
-@@ -265,11 +302,23 @@
+@@ -265,11 +406,37 @@
                        rv = chown(file, -1, grp->gr_gid);
                        break;
        }
@@ -617,21 +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) {
++/**
++ * 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;
-@@ -347,11 +396,11 @@
+@@ -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:
@@ -645,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,27 @@
+@@ -365,12 +542,49 @@
                        case ACT_SETENV:
                                last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1);
                                break;
@@ -654,15 +1974,23 @@ 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;
-+              }
-+      }
-+      
-+      return 0;
-+}
-+
-+int rule_flags(struct hotplug2_event_t *event, struct rule_t *rule) {
+               }
+       }
+       
+       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++) {
@@ -670,10 +1998,138 @@ diff -urN -x.svn hotplug2-0.9/rules.c hotplug2/rules.c
 +                      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;
        
-@@ -518,6 +588,9 @@
+@@ -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},
@@ -683,9 +2139,77 @@ diff -urN -x.svn hotplug2-0.9/rules.c hotplug2/rules.c
                /*symlink*/
                {"symlink", 2, ACT_SYMLINK},
                {"softlink", 2, ACT_SYMLINK},
-diff -urN -x.svn hotplug2-0.9/rules.h hotplug2/rules.h
+@@ -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-30 12:44:52.501430000 +0200
++++ 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 <...> */
@@ -700,38 +2224,40 @@ diff -urN -x.svn hotplug2-0.9/rules.h hotplug2/rules.h
  
  #define EVAL_MATCH                    1
  #define EVAL_NOT_MATCH                        0
-@@ -42,6 +45,11 @@
+@@ -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 +73,8 @@
+@@ -65,6 +72,8 @@
        
        struct action_t *actions;
        int actions_c;
 +
-+      int flags;
++      unsigned int flags;
  };
  
  struct rules_t {
-@@ -72,7 +82,9 @@
+@@ -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 *);
-+int rule_flags(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 *rules_from_config(char *, struct rules_t *);
  
-diff -urN -x.svn hotplug2-0.9/TODO hotplug2/TODO
+ #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-28 14:51:00.012934184 +0200
 @@ -0,0 +1 @@