X-Git-Url: https://git.rohieb.name/openwrt.git/blobdiff_plain/370c954b8174a26876dbfeed6d92ac035b90bfcc..15bb59764550087c442026f9aa3829c79881c249:/package/hotplug2/patches/100-svn_update.patch?ds=sidebyside

diff --git a/package/hotplug2/patches/100-svn_update.patch b/package/hotplug2/patches/100-svn_update.patch
index 2844c6eb4..283e61371 100644
--- a/package/hotplug2/patches/100-svn_update.patch
+++ b/package/hotplug2/patches/100-svn_update.patch
@@ -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 @@