1 diff -urN -x .svn hotplug2-0.9/AUTHORS hotplug2/AUTHORS
2 --- hotplug2-0.9/AUTHORS 2006-10-08 18:13:50.000000000 +0200
3 +++ hotplug2/AUTHORS 2007-06-30 12:59:20.459674000 +0200
7 iSteve <isteve@bofh.cz>
8 -Tomas Janousek <tomi@nomi.cz>
12 +nbd (rules override patch, various fixes, suggestions, testing etc.)
13 +Tomas Janousek <tomi@nomi.cz> (Makefiles, SVN hosting)
18 Randy Dunlap (help with isapnpmap)
19 Igor2 (provided testing machines)
20 yanek (provided testing machines)
21 +Zdenek Styblik (provided testing OpenWRT device)
22 +OpenWRT team (for trusting this project)
23 +mtu (debugging, testing, ideas)
24 +mnemoc (trivial sanity changes on makefiles, linux24 compat patches)
26 -...anyone taking more than a short peek at the software.
27 \ No newline at end of file
28 +...anyone taking more than a short peek at the software.
29 diff -urN -x .svn hotplug2-0.9/Changelog hotplug2/Changelog
30 --- hotplug2-0.9/Changelog 2006-10-08 15:32:31.000000000 +0200
31 +++ hotplug2/Changelog 2007-07-09 01:17:14.865503750 +0200
34 +* Add --set-rules-file.
36 +* Add 'printdebug' rule.
37 +* Fix chmod, chown, chgrp.
38 +* Use octal for chmod and makedev.
39 +* Add 'nothrottle' flag, allowing overriding max-children from a rule
44 * Use signals to handle children.
45 * Separate info and debugging output.
48 * Significant cleanup of rules handling.
49 * Better error reporting.
51 \ No newline at end of file
53 diff -urN -x .svn hotplug2-0.9/common.mak hotplug2/common.mak
54 --- hotplug2-0.9/common.mak 2006-09-26 01:03:08.000000000 +0200
55 +++ hotplug2/common.mak 2007-07-09 01:17:14.869504000 +0200
59 -CFLAGS=-Os -DHAVE_RULES -Wall -g
60 +CFLAGS=-Os -DHAVE_RULES -Wall -g -Wextra
63 INSTALL=install -c -m 644
65 .PHONY: all clean dep install install-recursive clean-recursive \
66 dep-recursive all-recursive
68 -MAKEDEP=-gcc $(CFLAGS) -MM $(wildcard *.c *.cc) > .depend
69 +MAKEDEP=-$(CC) $(CFLAGS) -MM $(wildcard *.c *.cc) > .depend
73 diff -urN -x .svn hotplug2-0.9/docs/hotplug2.8 hotplug2/docs/hotplug2.8
74 --- hotplug2-0.9/docs/hotplug2.8 2006-09-26 09:23:36.000000000 +0200
75 +++ hotplug2/docs/hotplug2.8 2007-06-28 14:50:59.874955160 +0200
78 \fB\-\-dumb\fR, \fB\-\-no\-dumb\fR
79 Run or do not run hotplug2 in dumb mode. Dumb mode means that rules are being ignored, the only action taken is mload modules to all devices whose uevent exports MODALIAS. Only available if compiled with HAVE_RULES.
80 +\fB\-\-override\fR, \fB\-\-no\-override\fR
81 +Allows hotplug2 behavior overriding for different rules, using various flags. See hotplug2 rules documentation for details. The default is not to allow overriding, the flags are therefore ignored.
83 \fB\-\-max\-children <value>\fR
84 Set the value of maximum children hotplug2 may have running simultaneously. Default is 20.
87 \fB\-\-set\-modprobe\-cmd <cmd>\fR
88 Sets the application used to perform modprobe. It only gets used in dumb mode. Default is to autodetect: if '/bin/modprobe' is from module\-init\-tools, use '/sbin/modprobe', otherwise use '/sbin/hotplug2\-modwrap'.
89 +\fB\-\-set\-rules\-file <file>\fR
90 +Sets the path to the file containing hotplug2 rules.
94 diff -urN -x .svn hotplug2-0.9/docs/hotplug2.rules.doc hotplug2/docs/hotplug2.rules.doc
95 --- hotplug2-0.9/docs/hotplug2.rules.doc 2006-09-26 10:19:46.000000000 +0200
96 +++ hotplug2/docs/hotplug2.rules.doc 2007-06-28 14:50:59.872955464 +0200
101 -Comments are allowed, they are prefixed with '#', treating the whole rest of the
102 -line as comment. Please note that comments in place of action parameters are not
104 +Comments are allowed, they are prefixed with '#', treating the whole rest of
105 +the line as comment. Please note that comments in place of action parameters
108 -The <key> is one of the environmental variables that have been obtained by the
110 +The <key> is one of the environmental variables that have been obtained by
119 - Execute an application using system();, takes one parameter. Note that
120 - the application has set all environmental variables read by uevent
122 + Execute an application using system();, takes one parameter. Note
123 + that the application has set all environmental variables read by
127 Break the processing of the current block of actions.
130 * exec <application [parameter [parameter [...]]]> ;
131 Execute an application. Takes variable number of parameters, but the
132 - last parameter must be terminated with semicolon. Again, all variables
133 - are set as environmental.
134 + last parameter must be terminated with semicolon. Again, all
135 + variables are set as environmental.
137 - If you need to escape the ';', use '\\;'. Only applies for actions with
138 - variable number of parameters.
139 + If you need to escape the ';', use '\\;'. Only applies for actions
140 + with variable number of parameters.
142 * makedev <path> <mode>
143 - Create a device with given mode. The mode is not in octal unless it
144 - starts with '0', eg. "0644" != "644".
145 + Create a device with given mode. Mode is interpreted as octal.
147 Major, minor and devpath must be set for makedev to be able to work.
148 Tests for these variables are also performed internally in makedev
151 * symlink <target> <linkname>
152 - Create a symbolic link (symlink, also known as soft link) pointing at
153 - target with name linkname.
154 + Create a symbolic link (symlink, also known as soft link) pointing
155 + at target with name linkname.
157 * chown <path> <owner name>
158 Change owner of path to owner name.
159 @@ -111,12 +110,32 @@
160 Change group of path to group name.
162 * chmod <path> <mode>
163 - Change mode of path to given mode. Like with makedev, heading '0' is
164 - necessary for the mode to be interpreted as octal.
165 + Change mode of path to given mode. Mode is interpreted as octal.
167 * setenv <key> <value>
168 Sets environmental variable key to the given value. If an env var
169 of the given name already exists, it gets overwritten.
172 + Prints all variables read from kernel.
177 +Flags are, syntactically, just like actions; their semantical value is different however.
178 +Instead of doing something, they instead change the general behavior of the processing
181 +Note that for flags to work, you also have to invoke it with --override.
183 +Currently, only one flag is implemented:
186 + Forcibly overrides hotplug2 throttling mechanism. If _all_ rules that match
187 + the given kernel event have 'nothrottle' set, hotplug2 will not wait for
188 + children count to get under max-children limit. That allows to throttle
189 + eg. helper application execution or modprobes, but yet keep node devices
198 -Below is a sample hotplug2.rules file. It loads modules to all available devices
199 -quietly and creates device nodes for block devices.
200 +Below is a sample hotplug2.rules file. It loads modules to all available
201 +devices quietly and creates device nodes for block devices. Note that this
202 +sample is not very viable for real life usage.
203 ---------------------------------------------------------------------------------
205 exec modprobe -q %MODALIAS% ;
207 SUBSYSTEM == block, DEVPATH is set, MAJOR is set, MINOR is set {
208 makedev /dev/%DEVICENAME% 0644
212 +Please find also the more complex set of rules, dedicated to handling most
214 +---------------------------------------------------------------------------------
220 +# Load modules (what old hotplug did)
222 + exec modprobe -q %MODALIAS% ;
225 +# Create device nodes
226 +DEVPATH is set, MAJOR is set, MINOR is set {
227 + makedev /dev/%DEVICENAME% 0644
230 +# Mount a USB flashdisk
231 +ACTION == add, PHYSDEVPATH ~~ "/usb[0-9]*/", DEVICENAME ~~ "^sd[a-z][0-9]+$", DEVPATH is set, MAJOR is set, MINOR is set {
232 + makedev /dev/%DEVICENAME% 0644
233 + exec mount /dev/%DEVICENAME% /mnt/%DEVICENAME%
236 +# Unmount a USB flashdisk
237 +ACTION == remove, PHYSDEVPATH ~~ "/usb[0-9]*/", DEVICENAME ~~ "^sd[a-z][0-9]+$", MAJOR is set, MINOR is set {
238 + exec umount /mnt/%DEVICENAME%
240 diff -urN -x .svn hotplug2-0.9/docs/Makefile hotplug2/docs/Makefile
241 --- hotplug2-0.9/docs/Makefile 2006-09-26 00:27:02.000000000 +0200
242 +++ hotplug2/docs/Makefile 2007-06-28 14:50:59.875955008 +0200
249 +MANDIR=/usr/share/man
254 - $(INSTALL) $(wildcard *.8) /usr/share/man/man8/
255 + $(INSTALL) $(wildcard *.8) $(DESTDIR)$(MANDIR)/man8/
258 include ../common.mak
259 diff -urN -x .svn hotplug2-0.9/examples/Makefile hotplug2/examples/Makefile
260 --- hotplug2-0.9/examples/Makefile 2006-09-26 01:03:08.000000000 +0200
261 +++ hotplug2/examples/Makefile 2007-06-28 14:50:59.991937376 +0200
267 +KERNELVER=`uname -r`
273 - case "`uname -r`" in \
275 - $(INSTALL) hotplug2.rules-2.6kernel /etc/hotplug2.rules \
278 - $(INSTALL) hotplug2.rules-2.4kernel /etc/hotplug2.rules \
281 + if ! [ -e "/etc/hotplug2.rules" ]; then \
282 + case "$(KERNELVER)" in \
284 + $(INSTALL) hotplug2.rules-2.6kernel $(DESTDIR)/etc/hotplug2.rules \
287 + $(INSTALL) hotplug2.rules-2.4kernel $(DESTDIR)/etc/hotplug2.rules \
293 include ../common.mak
294 diff -urN -x .svn hotplug2-0.9/filemap_utils.c hotplug2/filemap_utils.c
295 --- hotplug2-0.9/filemap_utils.c 2006-09-25 12:14:12.000000000 +0200
296 +++ hotplug2/filemap_utils.c 2007-07-09 02:01:10.966249750 +0200
299 #include "filemap_utils.h"
301 -int map_file(char *filename, struct filemap_t *filemap) {
303 + * Basic open/mmap wrapper to make things simpler.
305 + * @1 Filename of the mmaped file
306 + * @2 Pointer to filemap structure
308 + * Returns: 0 if success, 1 otherwise
310 +int map_file(const char *filename, struct filemap_t *filemap) {
313 filemap->fd = open(filename, O_RDONLY);
319 + * Basic close/munmap wrapper.
321 + * @1 Pointer to filemap structure
323 + * Returns: always 0
325 int unmap_file(struct filemap_t *filemap) {
326 - close(filemap->fd);
327 munmap(filemap->map, filemap->size);
328 + close(filemap->fd);
332 diff -urN -x .svn hotplug2-0.9/filemap_utils.h hotplug2/filemap_utils.h
333 --- hotplug2-0.9/filemap_utils.h 2006-09-25 22:24:36.000000000 +0200
334 +++ hotplug2/filemap_utils.h 2007-07-09 02:01:10.962249500 +0200
339 -int map_file(char *, struct filemap_t *);
340 +int map_file(const char *, struct filemap_t *);
341 int unmap_file(struct filemap_t *);
343 diff -urN -x .svn hotplug2-0.9/hotplug2.c hotplug2/hotplug2.c
344 --- hotplug2-0.9/hotplug2.c 2006-10-08 15:18:23.000000000 +0200
345 +++ hotplug2/hotplug2.c 2007-07-09 02:01:10.962249500 +0200
347 #include <linux/netlink.h>
349 #include "mem_utils.h"
350 +#include "filemap_utils.h"
351 #include "hotplug2.h"
352 +#include "hotplug2_utils.h"
354 #include "childlist.h"
358 highest_seqnum == get_kernel_seqnum())
361 + * These variables are accessed from throughout the code.
363 + * TODO: Move this into a hotplug2_t-like variable.
365 event_seqnum_t highest_seqnum = 0;
370 int max_child_c = 20;
375 char *modprobe_command = NULL;
378 + * Release all memory associated with an uevent read from kernel. The given
379 + * pointer is no longer valid, as it gets freed as well.
381 + * @1 The event that is to be freed.
385 inline void free_hotplug2_event(struct hotplug2_event_t *event) {
393 + * A trivial function determining the action that the uevent.
395 + * @1 String containing the action name (null-terminated).
397 + * Returns: Macro of the given action
399 inline int get_hotplug2_event_action(char *action) {
400 if (!strcmp(action, "add"))
403 return ACTION_UNKNOWN;
407 + * Looks up a value according to the given key.
409 + * @1 A hotplug event structure
410 + * @2 Key for lookup
412 + * Returns: The value of the key or NULL if no such key found
414 char *get_hotplug2_value_by_key(struct hotplug2_event_t *event, char *key) {
421 -inline int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) {
423 + * Appends a key-value pair described by the second argument to the
426 + * @1 A hotplug event structure
427 + * @1 An item in format "key=value" to be appended
429 + * Returns: 0 if success, -1 if the string is malformed
431 +int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) {
434 ptr = strchr(item, '=');
438 * Variables not generated by kernel but demanded nonetheless...
440 + * TODO: Split this to a different function
442 if (!strcmp(item, "DEVPATH")) {
449 + * Duplicates all allocated memory of a source hotplug event
450 + * and returns a new hotplug event, an identical copy of the
453 + * @1 Source hotplug event structure
455 + * Returns: A copy of the source event structure
457 inline struct hotplug2_event_t *dup_hotplug2_event(struct hotplug2_event_t *src) {
458 struct hotplug2_event_t *dest;
465 + * Parses a string into a hotplug event structurs.
467 + * @1 The event string (not null terminated)
468 + * @2 The size of the event string
470 + * Returns: A new event structure
472 inline struct hotplug2_event_t *get_hotplug2_event(char *event_str, int size) {
474 struct hotplug2_event_t *event;
475 @@ -161,59 +220,15 @@
479 -inline event_seqnum_t get_kernel_seqnum() {
485 - strcpy(filename, sysfs_seqnum_path);
487 - fp = fopen(filename, "r");
491 - fread(seqnum, 1, 64, fp);
494 - return strtoull(seqnum, NULL, 0);
497 -inline int init_netlink_socket() {
498 - int netlink_socket;
499 - struct sockaddr_nl snl;
500 - int buffersize = 16 * 1024 * 1024;
502 - memset(&snl, 0x00, sizeof(struct sockaddr_nl));
503 - snl.nl_family = AF_NETLINK;
504 - snl.nl_pid = getpid();
506 - netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
507 - if (netlink_socket == -1) {
508 - ERROR("opening netlink","Failed socket: %s.", strerror(errno));
512 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) {
513 - ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
515 - /* Somewhat safe default. */
516 - buffersize = 106496;
518 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize))) {
519 - ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
523 - if (bind(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
524 - ERROR("opening netlink","Failed bind: %s.", strerror(errno));
525 - close(netlink_socket);
529 - return netlink_socket;
533 + * Evaluates an argument into a true/false value.
537 + * @3 pointer to output value
539 + * Returns: 0 if success, -1 otherwise
541 int get_bool_opt(char *argv, char *name, int *value) {
548 -void cleanup(void) {
550 + * Performs a cleanup; closes uevent socket, resets signal
551 + * handlers, waits for all the children.
558 close(netlink_socket);
560 INFO("cleanup", "All children terminated.");
564 + * Handles all signals.
566 + * @1 Signal identifier
570 void sighandler(int sig) {
578 + * Execute all rules for this particular event.
580 + * @1 Hotplug event structure
581 + * @2 Rules structure, containing array of rules
585 void perform_action(struct hotplug2_event_t *event, struct rules_t *rules) {
588 @@ -324,13 +360,72 @@
590 free_hotplug2_event(event);
594 + * Iterates through all rules, and performs an AND between all flags that
595 + * would apply during execution (ie. all rules that have conditions matching
596 + * the hotplug event).
598 + * @1 Hotplug event structure
599 + * @2 Rules structure, containing array of rules
601 + * Returns: Flags that apply to all matching rules
603 +int flags_eval(struct hotplug2_event_t *event, struct rules_t *rules) {
604 + int flags = FLAG_ALL;
608 + for (i = 0; i < rules->rules_c; i++) {
611 + for (j = 0; j < rules->rules[i].conditions_c; j++) {
612 + if (rule_condition_eval(event, &rules->rules[i].conditions[j]) != EVAL_MATCH) {
619 + * Logical AND between flags we've got already and
620 + * those we're adding.
623 + rule_flags(&rules->rules[i]);
624 + flags &= rules->rules[i].flags;
629 + * A little trick; if no rule matched, we return FLAG_ALL
630 + * and have it skipped completely.
636 +#define perform_action(event, rules)
640 + * Blindly modprobe the modalias, nothing more.
642 + * @1 Hotplug event structure
643 + * @2 Modalias to be loaded
647 void perform_dumb_action(struct hotplug2_event_t *event, char *modalias) {
648 free_hotplug2_event(event);
649 execl(modprobe_command, modprobe_command, "-q", modalias, NULL);
653 + * Attempt to figure out whether our modprobe command can handle modalias.
654 + * If not, use our own wrapper.
656 + * Returns: 0 if success, -1 otherwise
658 int get_modprobe_command() {
664 int main(int argc, char *argv[]) {
668 static char buffer[UEVENT_BUFFER_SIZE+512];
669 struct hotplug2_event_t *tmpevent;
670 char *modalias, *seqnum;
671 @@ -390,28 +488,39 @@
675 + unsigned int flags;
676 char *coldplug_command = NULL;
677 + char *rules_file = HOTPLUG2_RULE_PATH;
680 struct rules_t *rules = NULL;
681 - struct stat statbuf;
684 + struct filemap_t filemap;
686 struct options_t bool_options[] = {
687 {"persistent", &persistent},
688 {"coldplug", &coldplug},
689 {"udevtrigger", &coldplug}, /* compatibility */
690 + {"override", &override},
698 + * We parse all the options...
700 for (argc--; argc > 0; argc--) {
705 for (i = 0; bool_options[i].name != NULL; i++) {
706 if (!get_bool_opt(*argv, bool_options[i].name, bool_options[i].value)) {
708 + * Bool options are --option or --no-options. If we handled
709 + * it, quit iterating.
713 if (!strcmp(*argv, "--max-children")) {
714 @@ -435,52 +544,52 @@
717 modprobe_command = *argv;
718 + } else if (!strcmp(*argv, "--set-rules-file")) {
724 + rules_file = *argv;
733 + * We don't use rules, so we use dumb mode only.
738 + * We're not in dumb mode, parse the rules. If we fail,
739 + * faillback to dumb mode.
742 - filemap = MAP_FAILED;
743 - rule_fd = open(HOTPLUG2_RULE_PATH, O_RDONLY | O_NOATIME);
744 - if (rule_fd == -1) {
746 - ERROR("rules parse","Unable to open rules file: %s.", strerror(errno));
750 - if (fstat(rule_fd, &statbuf)) {
752 - ERROR("rules parse","Unable to stat rules file: %s.", strerror(errno));
756 - filemap = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, rule_fd, 0);
757 - if (filemap == MAP_FAILED) {
758 + if (map_file(rules_file, &filemap)) {
759 + ERROR("rules parse","Unable to open/mmap rules file.");
761 - ERROR("rules parse","Unable to mmap rules file: %s.", strerror(errno));
765 - rules = rules_from_config((char*)filemap);
766 + rules = rules_from_config((char*)(filemap.map), NULL);
768 ERROR("rules parse","Unable to parse rules file.");
772 + unmap_file(&filemap);
775 - if (filemap != MAP_FAILED)
776 - munmap(filemap, statbuf.st_size);
781 ERROR("rules parse","Parsing rules failed, switching to dumb mode.");
782 - } else if (!modprobe_command)
784 - if (dumb && !modprobe_command)
788 + * No modprobe command specified, let's autodetect it.
790 + if (!modprobe_command)
792 if (get_modprobe_command()) {
793 ERROR("modprobe_command","Unable to autodetect modprobe command.");
795 DBG("modprobe_command", "Using modprobe: `%s'.", modprobe_command);
798 - netlink_socket = init_netlink_socket();
800 + * Open netlink socket to read the uevents
802 + netlink_socket = init_netlink_socket(NETLINK_BIND);
804 if (netlink_socket == -1) {
805 ERROR("netlink init","Unable to open netlink socket.");
807 signal(SIGINT, sighandler);
808 signal(SIGCHLD, sighandler);
811 + * If we desire coldplugging, we initiate it right now.
814 if (coldplug_command == NULL)
815 coldplug_command = UDEVTRIGGER_COMMAND;
816 @@ -523,10 +638,19 @@
817 coldplug_p = FORK_FINISHED;
821 + * Main loop reading uevents
825 + * Read the uevent packet
827 size = recv(netlink_socket, &buffer, sizeof(buffer), 0);
831 + * Parse the event into an event structure
833 tmpevent = get_hotplug2_event(buffer, size);
835 if (tmpevent == NULL) {
836 @@ -534,26 +658,61 @@
841 + * Look up two important items of the event
843 modalias = get_hotplug2_value_by_key(tmpevent, "MODALIAS");
844 seqnum = get_hotplug2_value_by_key(tmpevent, "SEQNUM");
848 + * Seqnum is necessary not to end up in a race with the kernel.
850 if (seqnum == NULL) {
851 free_hotplug2_event(tmpevent);
852 ERROR("reading events", "Malformed event read (missing SEQNUM).");
857 + * Maintain seqnum continuity
859 cur_seqnum = strtoull(seqnum, NULL, 0);
860 if (cur_seqnum > highest_seqnum)
861 highest_seqnum = cur_seqnum;
863 - if (tmpevent->action == ACTION_ADD && (!dumb || modalias != NULL)) {
865 + * If we are in smart mode, we'll always pass. If we're in dumb mode,
866 + * we only pass events that have 'add' action and have modalias set.
868 + if ((dumb && tmpevent->action == ACTION_ADD && modalias != NULL) || (!dumb)) {
870 + * Pre-evaluation of the flags
872 + if (!dumb && override) {
873 + flags = flags_eval(tmpevent, rules);
875 + DBG("flags", "flag returned: %8x", flags);
877 + if (flags == FLAG_ALL)
880 + flags = FLAG_UNSET;
884 * We have more children than we want. Wait until SIGCHLD handler reduces
887 + * Unless, of course, we've specified otherwise and no rules that match
890 - while (child_c >= max_child_c) {
891 - usleep(HOTPLUG2_THROTTLE_INTERVAL);
892 + if (!flags & FLAG_NOTHROTTLE) {
894 + * Okay, throttle away!
896 + while (child_c >= max_child_c) {
897 + usleep(HOTPLUG2_THROTTLE_INTERVAL);
901 sigemptyset(&block_mask);
902 @@ -562,17 +721,18 @@
906 - ERROR("event","fork failed: %s.", strerror(errno));
907 + ERROR("event", "fork failed: %s.", strerror(errno));
911 + * TODO: We do not have to dup here, or do we?
913 sigprocmask(SIG_UNBLOCK, &block_mask, 0);
914 signal(SIGCHLD, SIG_DFL);
915 signal(SIGUSR1, SIG_DFL);
918 perform_action(dup_hotplug2_event(tmpevent), rules);
921 perform_dumb_action(dup_hotplug2_event(tmpevent), modalias);
924 @@ -593,12 +753,10 @@
925 signal(SIGINT, SIG_DFL);
926 signal(SIGCHLD, SIG_DFL);
937 diff -urN -x .svn hotplug2-0.9/hotplug2-dnode.c hotplug2/hotplug2-dnode.c
938 --- hotplug2-0.9/hotplug2-dnode.c 2006-09-26 17:35:35.000000000 +0200
939 +++ hotplug2/hotplug2-dnode.c 2007-07-09 01:17:14.869504000 +0200
942 #include "mem_utils.h"
943 #include "hotplug2.h"
944 +#include "hotplug2_utils.h"
945 #include "parser_utils.h"
947 #define MODALIAS_MAX_LEN 1024
950 #define TEST_INPUT_BIT(i,bm) (bm[i / BITS_PER_LONG] & (((unsigned long)1) << (i%BITS_PER_LONG)))
952 -int init_netlink_socket() {
953 - int netlink_socket;
954 - struct sockaddr_nl snl;
955 - int buffersize = 16 * 1024 * 1024;
957 - memset(&snl, 0x00, sizeof(struct sockaddr_nl));
958 - snl.nl_family = AF_NETLINK;
959 - snl.nl_pid = getpid();
961 - netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
962 - if (netlink_socket == -1) {
963 - ERROR("opening netlink","Failed socket: %s.", strerror(errno));
967 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUFFORCE, &buffersize, sizeof(buffersize))) {
968 - ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
970 - /* Somewhat safe default. */
971 - buffersize = 106496;
973 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUF, &buffersize, sizeof(buffersize))) {
974 - ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
978 - if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
979 - ERROR("opening netlink","Failed bind: %s.", strerror(errno));
980 - close(netlink_socket);
984 - return netlink_socket;
987 -inline event_seqnum_t get_kernel_seqnum() {
993 - strcpy(filename, sysfs_seqnum_path);
995 - fp = fopen(filename, "r");
999 - fread(seqnum, 1, 64, fp);
1002 - return strtoull(seqnum, NULL, 0);
1006 + * Parses a bitmap; output is a list of offsets of bits of a bitmap
1007 + * of arbitrary size that are set to 1.
1009 + * @1 Name of the bitmap parsed
1010 + * @2 The actual bitmap pointer
1011 + * @3 Lower boundary of the bitmap
1012 + * @4 Upper boundary of the bitmap
1014 + * Returns: Newly allocated string containing the offsets
1016 char *bitmap_to_bitstring(char name, unsigned long *bm, unsigned int min_bit, unsigned int max_bit)
1024 + * Reverses the bitmap_to_bitstring function.
1026 + * @1 Bitstring to be converted
1027 + * @2 Output bitmap
1028 + * @3 Size of the whole bitmap
1032 void string_to_bitmap(char *input, unsigned long *bitmap, int bm_len) {
1035 @@ -146,6 +114,14 @@
1037 bitmap = bitmap_to_bitstring(name, bitmap ## _bits, min, mapkey ## _MAX);
1040 + * Creates an input modalias out of preset environmental variables.
1042 + * @1 Pointer to where modalias will be created
1043 + * @2 Maximum size of the modalias
1045 + * Returns: 0 if success, -1 otherwise
1047 int get_input_modalias(char *modalias, int modalias_len) {
1050 @@ -245,6 +221,14 @@
1052 #undef TEST_INPUT_BIT
1055 + * Creates a PCI modalias out of preset environmental variables.
1057 + * @1 Pointer to where modalias will be created
1058 + * @2 Maximum size of the modalias
1060 + * Returns: 0 if success, -1 otherwise
1062 int get_pci_modalias(char *modalias, int modalias_len) {
1063 char *class_env, *id_env, *subsys_env;
1065 @@ -290,6 +274,15 @@
1070 + * Creates an IEEE1394 (FireWire) modalias out of preset environmental
1073 + * @1 Pointer to where modalias will be created
1074 + * @2 Maximum size of the modalias
1076 + * Returns: 0 if success, -1 otherwise
1078 int get_ieee1394_modalias(char *modalias, int modalias_len) {
1079 char *vendor_env, *model_env;
1080 char *specifier_env, *version_env;
1081 @@ -317,6 +310,14 @@
1086 + * Creates a serio modalias out of preset environmental variables.
1088 + * @1 Pointer to where modalias will be created
1089 + * @2 Maximum size of the modalias
1091 + * Returns: 0 if success, -1 otherwise
1093 int get_serio_modalias(char *modalias, int modalias_len) {
1094 char *serio_type_env, *serio_proto_env;
1095 char *serio_id_env, *serio_extra_env;
1096 @@ -344,6 +345,14 @@
1101 + * Creates an USB modalias out of preset environmental variables.
1103 + * @1 Pointer to where modalias will be created
1104 + * @2 Maximum size of the modalias
1106 + * Returns: 0 if success, -1 otherwise
1108 int get_usb_modalias(char *modalias, int modalias_len) {
1109 char *product_env, *type_env, *interface_env;
1111 @@ -409,6 +418,16 @@
1116 + * Distributes modalias generating according to the bus name.
1119 + * @2 Pointer to where modalias will be created
1120 + * @3 Maximum size of the modalias
1122 + * Returns: The return value of the subsystem modalias function, or -1 if
1125 int get_modalias(char *bus, char *modalias, int modalias_len) {
1126 memset(modalias, 0, modalias_len);
1128 @@ -435,6 +454,16 @@
1133 + * Turns all environmental variables as set when invoked by /proc/sys/hotplug
1134 + * into an uevent formatted (thus not null-terminated) string.
1136 + * @1 All environmental variables
1137 + * @2 Bus of the event (as read from argv)
1138 + * @3 Pointer to size of the returned uevent string
1140 + * Returns: Not null terminated uevent string.
1142 inline char *get_uevent_string(char **environ, char *bus, unsigned long *uevent_string_len) {
1143 char *uevent_string;
1149 - netlink_socket = init_netlink_socket();
1150 + netlink_socket = init_netlink_socket(NETLINK_CONNECT);
1151 if (netlink_socket == -1) {
1152 ERROR("netlink init","Unable to open netlink socket.");
1154 diff -urN -x .svn hotplug2-0.9/hotplug2.h hotplug2/hotplug2.h
1155 --- hotplug2-0.9/hotplug2.h 2006-10-08 12:21:56.000000000 +0200
1156 +++ hotplug2/hotplug2.h 2007-07-09 01:17:14.865503750 +0200
1161 -#define O_NOATIME 01000000
1162 +#define O_NOATIME 01000000
1165 #define ERROR(action, fmt, arg...) fprintf(stderr, "[%s]: " fmt"\n", action, ##arg);
1168 #define UEVENT_BUFFER_SIZE 2048
1169 #define HOTPLUG2_POLL_INTERVAL 20000
1170 -#define HOTPLUG2_THROTTLE_INTERVAL 10000
1171 +#define HOTPLUG2_THROTTLE_INTERVAL 10000
1172 #define HOTPLUG2_RULE_PATH "/etc/hotplug2.rules"
1174 #define ACTION_ADD 0
1175 diff -urN -x .svn hotplug2-0.9/hotplug2_utils.c hotplug2/hotplug2_utils.c
1176 --- hotplug2-0.9/hotplug2_utils.c 1970-01-01 01:00:00.000000000 +0100
1177 +++ hotplug2/hotplug2_utils.c 2007-07-09 01:17:14.869504000 +0200
1180 +#include <string.h>
1181 +#include <stdlib.h>
1184 +#include <unistd.h>
1186 +#include <sys/socket.h>
1187 +#include <sys/types.h>
1188 +#include <sys/un.h>
1189 +#include <sys/wait.h>
1190 +#include <linux/types.h>
1191 +#include <linux/netlink.h>
1193 +#include "hotplug2_utils.h"
1196 + * A trivial function that reads kernel seqnum from sysfs.
1198 + * Returns: Seqnum as read from sysfs
1200 +inline event_seqnum_t get_kernel_seqnum() {
1203 + char filename[64];
1206 + strcpy(filename, sysfs_seqnum_path);
1208 + fp = fopen(filename, "r");
1212 + fread(seqnum, 1, 64, fp);
1215 + return strtoull(seqnum, NULL, 0);
1219 + * Opens a PF_NETLINK socket into the kernel, to read uevents.
1221 + * @1 Specifies type of socket (whether we bind or whether we connect)
1223 + * Returns: Socket fd if succesful, -1 otherwise.
1225 +inline int init_netlink_socket(int type) {
1226 + int netlink_socket;
1227 + struct sockaddr_nl snl;
1228 + int buffersize = 16 * 1024 * 1024;
1230 + memset(&snl, 0x00, sizeof(struct sockaddr_nl));
1231 + snl.nl_family = AF_NETLINK;
1232 + snl.nl_pid = getpid();
1233 + snl.nl_groups = 1;
1234 + netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
1235 + if (netlink_socket == -1) {
1236 + ERROR("opening netlink","Failed socket: %s.", strerror(errno));
1241 + * We're trying to override buffer size. If we fail, we attempt to set a big buffer and pray.
1243 + if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) {
1244 + ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
1246 + /* Somewhat safe default. */
1247 + buffersize = 106496;
1249 + if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize))) {
1250 + ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
1255 + * hotplug2-dnode performs connect, while hotplug2 daemon binds
1258 + case NETLINK_CONNECT:
1259 + if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
1260 + ERROR("opening netlink","Failed connect: %s.", strerror(errno));
1261 + close(netlink_socket);
1266 + if (bind(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
1267 + ERROR("opening netlink","Failed bind: %s.", strerror(errno));
1268 + close(netlink_socket);
1273 + return netlink_socket;
1275 diff -urN -x .svn hotplug2-0.9/hotplug2_utils.h hotplug2/hotplug2_utils.h
1276 --- hotplug2-0.9/hotplug2_utils.h 1970-01-01 01:00:00.000000000 +0100
1277 +++ hotplug2/hotplug2_utils.h 2007-07-09 01:17:14.869504000 +0200
1279 +/*****************************************************************************\
1281 +* | || | ___ | |_ _ __ | | _ _ __ _ |_ ) *
1282 +* | __ |/ _ \| _|| '_ \| || || |/ _` | / / *
1283 +* |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| *
1285 +\*****************************************************************************/
1287 +#ifndef HOTPLUG2_UTILS_H
1288 +#define HOTPLUG2_UTILS_H 1
1290 +#include "hotplug2.h"
1292 +#define NETLINK_UNDEFINED 0
1293 +#define NETLINK_CONNECT 1
1294 +#define NETLINK_BIND 2
1296 +inline event_seqnum_t get_kernel_seqnum();
1297 +inline int init_netlink_socket(int);
1300 diff -urN -x .svn hotplug2-0.9/linux24_compat/hotplug2-coldplug-2.4.c hotplug2/linux24_compat/hotplug2-coldplug-2.4.c
1301 --- hotplug2-0.9/linux24_compat/hotplug2-coldplug-2.4.c 2006-09-25 22:22:47.000000000 +0200
1302 +++ hotplug2/linux24_compat/hotplug2-coldplug-2.4.c 2007-07-09 01:17:14.793499250 +0200
1304 #include "../mem_utils.h"
1305 #include "../parser_utils.h"
1306 #include "../filemap_utils.h"
1308 -inline int init_netlink_socket() {
1309 - int netlink_socket;
1310 - struct sockaddr_nl snl;
1311 - int buffersize = 16 * 1024 * 1024;
1313 - memset(&snl, 0x00, sizeof(struct sockaddr_nl));
1314 - snl.nl_family = AF_NETLINK;
1315 - snl.nl_pid = getpid();
1316 - snl.nl_groups = 1;
1317 - netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT);
1318 - if (netlink_socket == -1) {
1319 - ERROR("opening netlink","Failed socket: %s.", strerror(errno));
1323 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUFFORCE, &buffersize, sizeof(buffersize))) {
1324 - ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno));
1326 - /* Somewhat safe default. */
1327 - buffersize = 106496;
1329 - if (setsockopt(netlink_socket, SOL_SOCKET, SO_SNDBUF, &buffersize, sizeof(buffersize))) {
1330 - ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno));
1334 - if (connect(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) {
1335 - ERROR("opening netlink","Failed bind: %s.", strerror(errno));
1336 - close(netlink_socket);
1340 - return netlink_socket;
1343 -inline event_seqnum_t get_kernel_seqnum() {
1346 - char filename[64];
1349 - strcpy(filename, sysfs_seqnum_path);
1351 - fp = fopen(filename, "r");
1355 - fread(seqnum, 1, 64, fp);
1358 - return strtoull(seqnum, NULL, 0);
1360 +#include "../hotplug2_utils.h"
1362 inline char *get_uevent_string(char **environ, unsigned long *uevent_string_len) {
1363 char *uevent_string;
1365 int main(int argc, char *argv[], char **environ) {
1368 - netlink_socket = init_netlink_socket();
1369 + netlink_socket = init_netlink_socket(NETLINK_CONNECT);
1370 if (netlink_socket == -1) {
1371 ERROR("netlink init","Unable to open netlink socket.");
1373 diff -urN -x .svn hotplug2-0.9/linux24_compat/hotplug2-modwrap.c hotplug2/linux24_compat/hotplug2-modwrap.c
1374 --- hotplug2-0.9/linux24_compat/hotplug2-modwrap.c 2006-09-25 22:23:07.000000000 +0200
1375 +++ hotplug2/linux24_compat/hotplug2-modwrap.c 2007-07-09 01:17:14.789499000 +0200
1377 #include "../parser_utils.h"
1378 #include "../filemap_utils.h"
1380 +#define MODULES_PATH "/lib/modules/"
1381 +#define MODULES_ALIAS "modules.alias"
1384 + * A simple fork/exec wrapper
1386 + * @1 Complete argv, including app path
1388 + * Returns: -1 if error, children return value otherwise
1390 int execute(char **argv) {
1400 - waitpid(p, NULL, 0);
1401 + waitpid(p, &status, 0);
1406 + return WEXITSTATUS(status);
1409 int main(int argc, char *argv[]) {
1412 match_alias = strdup(argv[argc - 1]);
1415 + * If we can't do uname, we're absolutely screwed and there's no
1416 + * sense thinking twice about anything.
1418 if (uname(&unamebuf)) {
1419 ERROR("uname", "Unable to perform uname: %s.", strerror(errno));
1423 - /* We use this one */
1425 + * We allow setting the modprobe command to an arbitrary value.
1427 + * The whole trick lies in executing modprobe with exactly the
1428 + * same argv as this app was executed, except we use a different
1429 + * argv[0] (application path) and argv[argc-1] (we substitute
1430 + * the given modalias by the matching module name)
1432 argv[0] = getenv("MODPROBE_COMMAND");
1433 if (argv[0] == NULL)
1434 argv[0] = "/sbin/modprobe";
1436 - /* "/lib/modules/" + "/" + "\0" */
1437 - filename = xmalloc(15 + strlen(unamebuf.release) + strlen("modules.alias"));
1438 - strcpy(filename, "/lib/modules/");
1441 + * Compose a path, /lib/modules/`uname -r`/modules.alias
1443 + * "/lib/modules/" + "/" + "\0"
1445 + filename = xmalloc(strlen(MODULES_PATH) + strlen(unamebuf.release) + strlen(MODULES_ALIAS));
1446 + strcpy(filename, MODULES_PATH);
1447 strcat(filename, unamebuf.release);
1448 - strcat(filename, "/modules.alias");
1449 + strcat(filename, MODULES_ALIAS);
1451 if (map_file(filename, &aliasmap)) {
1452 ERROR("map_file", "Unable to map file: `%s'.", filename);
1453 @@ -86,10 +113,16 @@
1458 + * Read all the aliases, match them against given parameter.
1460 nptr = aliasmap.map;
1461 while ((line = dup_line(nptr, &nptr)) != NULL) {
1465 + * We want aliases only
1467 token = dup_token(nline, &nline, isspace);
1468 if (!token || strcmp(token, "alias")) {
1470 @@ -98,12 +131,18 @@
1475 + * It's an alias, so fetch it
1477 cur_alias = dup_token(nline, &nline, isspace);
1484 + * And now we get the module name
1486 module = dup_token(nline, &nline, isspace);
1489 @@ -111,10 +150,14 @@
1494 + * If we match, we do the modalias->module name
1495 + * substitution as described above and execute.
1497 if (!fnmatch(cur_alias, match_alias, 0)) {
1498 argv[argc - 1] = module;
1499 if (execute(argv)) {
1500 - ERROR("execute", "Unable to execute: `%s'.", argv[0]);
1501 + ERROR("execute", "Error during exection of: `%s'.", argv[0]);
1505 @@ -122,6 +165,17 @@
1511 + * Perhaps we didn't match anything, so we might've been given
1512 + * a module name instead of a modalias. Try to modprobe it
1515 + if (strcmp(argv[argc - 1], match_alias) == 0) {
1516 + if (execute(argv)) {
1517 + ERROR("execute", "Error during exection of: `%s'.", argv[0]);
1523 diff -urN -x .svn hotplug2-0.9/linux24_compat/Makefile hotplug2/linux24_compat/Makefile
1524 --- hotplug2-0.9/linux24_compat/Makefile 2006-09-26 00:26:46.000000000 +0200
1525 +++ hotplug2/linux24_compat/Makefile 2007-07-09 01:17:14.793499250 +0200
1528 BINS=generate_alias hotplug2-coldplug-2.4 hotplug2-modwrap
1536 - $(INSTALL_BIN) hotplug2-coldplug-2.4 hotplug2-modwrap /sbin/
1537 - $(INSTALL_BIN) generate_alias /usr/sbin/
1538 + $(INSTALL_BIN) hotplug2-coldplug-2.4 hotplug2-modwrap $(DESTDIR)/sbin/
1539 + $(INSTALL_BIN) generate_alias $(DESTDIR)/usr/sbin/
1542 -hotplug2-coldplug-2.4: hotplug2-coldplug-2.4.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
1543 +hotplug2-coldplug-2.4: hotplug2-coldplug-2.4.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o ../hotplug2_utils.o
1544 hotplug2-modwrap: hotplug2-modwrap.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
1545 generate_alias: generate_alias.o ../parser_utils.o ../filemap_utils.o ../mem_utils.o
1547 diff -urN -x .svn hotplug2-0.9/Makefile hotplug2/Makefile
1548 --- hotplug2-0.9/Makefile 2006-09-26 01:03:08.000000000 +0200
1549 +++ hotplug2/Makefile 2007-07-09 01:17:14.869504000 +0200
1552 BINS=hotplug2 hotplug2-dnode
1553 SUBDIRS=linux24_compat docs examples
1560 - $(INSTALL_BIN) $(BINS) /sbin/
1561 + $(INSTALL_BIN) $(BINS) $(DESTDIR)/sbin/
1564 -hotplug2: hotplug2.o childlist.o mem_utils.o rules.o
1565 -hotplug2-dnode: hotplug2-dnode.o mem_utils.o parser_utils.o
1566 +hotplug2: hotplug2.o hotplug2_utils.o childlist.o mem_utils.o rules.o filemap_utils.o
1567 +hotplug2-dnode: hotplug2-dnode.o hotplug2_utils.o mem_utils.o parser_utils.o
1571 diff -urN -x .svn hotplug2-0.9/mem_utils.c hotplug2/mem_utils.c
1572 --- hotplug2-0.9/mem_utils.c 2006-09-25 22:21:45.000000000 +0200
1573 +++ hotplug2/mem_utils.c 2007-07-09 01:17:14.865503750 +0200
1579 + * A malloc wrapper. Exits if no memory.
1581 + * @1 Ammount of memory to allocate
1583 + * Returns: Pointer to freshly allocated memory
1585 inline void *xmalloc(size_t size) {
1593 + * A realloc wrapper. Exits if no memory.
1596 + * @2 Ammount of memory to allocate
1598 + * Returns: Pointer to reallocated memory
1600 inline void *xrealloc(void *inptr, size_t size) {
1602 ptr = realloc(inptr, size);
1603 diff -urN -x .svn hotplug2-0.9/parser_utils.c hotplug2/parser_utils.c
1604 --- hotplug2-0.9/parser_utils.c 2006-09-25 22:21:13.000000000 +0200
1605 +++ hotplug2/parser_utils.c 2007-07-09 01:17:14.865503750 +0200
1607 #include "mem_utils.h"
1608 #include "parser_utils.h"
1611 + * Creates a newly allocated null-terminated string representing line
1612 + * starting at a given pointer and ending at the closest newline. If
1613 + * no newline present, returns NULL. TODO, use dup_token
1615 + * @1 Starting pointer
1616 + * @2 Pointer where the end position is returned
1618 + * Returns: Newly allocated string containing the line or NULL
1620 char *dup_line(char *start, char **nptr) {
1628 + * Returns a token delimited by the given function.
1630 + * @1 Starting pointer
1631 + * @2 Pointer where the end position is returned
1632 + * @3 Function that identifies the delimiter characters
1634 + * Returns: Newly allocated string containing the token or NULL
1636 char *dup_token(char *start, char **nptr, int (*isdelimiter)(int)) {
1644 + * Returns the last token delimited by the given function.
1646 + * @1 Starting pointer of the whole string
1647 + * @2 Starting position
1648 + * @3 Pointer where the end position is returned
1649 + * @4 Function that identifies the delimiter characters
1651 + * Returns: Newly allocated string containing the token or NULL
1653 char *dup_token_r(char *start, char *start_string, char **nptr, int (*isdelimiter)(int)) {
1656 diff -urN -x .svn hotplug2-0.9/rules.c hotplug2/rules.c
1657 --- hotplug2-0.9/rules.c 2006-09-29 22:19:31.000000000 +0200
1658 +++ hotplug2/rules.c 2007-07-09 02:01:10.962249500 +0200
1660 #include <sys/stat.h>
1662 #include "mem_utils.h"
1663 +#include "filemap_utils.h"
1664 #include "hotplug2.h"
1667 -#define last_rule return_rules->rules[return_rules->rules_c - 1]
1670 + * Function supplementing 'mkdir -p'.
1672 + * @1 Path to be mkdir'd
1676 static void mkdir_p(char *path) {
1678 struct stat statbuf;
1684 + * Function supplementing 'rmdir -p'.
1686 + * @1 Path to be rmdir'd
1690 +static void rmdir_p(char *path) {
1693 + path = strdup(path);
1695 + while (ptr != NULL) {
1696 + ptr = strrchr(path, '/');
1709 + * Replaces all needles by a given value.
1711 + * @1 Haystack (which gets free'd in the function)
1713 + * @3 Needle replacement
1715 + * Returns: Newly allocated haysteck after replacement.
1717 static char *replace_str(char *hay, char *needle, char *replacement) {
1718 char *ptr, *start, *bptr, *buf;
1720 @@ -128,7 +169,15 @@
1724 -inline int isescaped(char *hay, char *ptr) {
1726 + * Trivial utility, figuring out whether a character is escaped or not.
1729 + * @2 Pointer to the character in question
1731 + * Returns: 1 if escaped, 0 otherwise
1733 +static inline int isescaped(char *hay, char *ptr) {
1737 @@ -138,6 +187,15 @@
1742 + * Performs replacement of all keys by their value based on the hotplug
1743 + * event structure. Keys are identified as strings %KEY%.
1746 + * @2 Hotplug event structure
1748 + * Returns: Newly allocated haystack (old is freed)
1750 static char *replace_key_by_value(char *hay, struct hotplug2_event_t *event) {
1751 char *sptr = hay, *ptr = hay;
1752 char *buf, *replacement;
1753 @@ -171,6 +229,17 @@
1758 + * Obtains all information from hotplug event structure about a device node.
1759 + * Creates the device node at a given path (expandable by keys) and with
1762 + * @1 Hotplug event structure
1763 + * @2 Path (may contain keys)
1764 + * @3 Mode of the file
1766 + * Returns: 0 if success, non-zero otherwise
1768 static int make_dev_from_event(struct hotplug2_event_t *event, char *path, mode_t devmode) {
1769 char *subsystem, *major, *minor, *devpath;
1771 @@ -196,12 +265,27 @@
1772 path = replace_key_by_value(path, event);
1774 rv = mknod(path, devmode, makedev(atoi(major), atoi(minor)));
1777 + * Fixes an issue caused by devmode being modified by umask.
1779 + chmod(path, devmode);
1788 + * Execute an application without invoking a shell.
1790 + * @1 Hotplug event structure
1791 + * @2 Path to the application, with expandable keys
1792 + * @3 Argv for the application, with expandable keys
1794 + * Returns: Exit status of the application.
1796 static int exec_noshell(struct hotplug2_event_t *event, char *application, char **argv) {
1799 @@ -211,11 +295,12 @@
1803 + application = replace_key_by_value(strdup(application), event);
1804 for (i = 0; argv[i] != NULL; i++) {
1805 argv[i] = replace_key_by_value(argv[i], event);
1807 execvp(application, argv);
1812 if (waitpid(p, &status, 0) == -1)
1813 @@ -226,6 +311,14 @@
1818 + * Execute an application while invoking a shell.
1820 + * @1 Hotplug event structure
1821 + * @2 The application and all its arguments, with expandable keys
1823 + * Returns: Exit status of the application.
1825 static int exec_shell(struct hotplug2_event_t *event, char *application) {
1828 @@ -235,6 +328,15 @@
1833 + * Create a symlink, with necessary parent directories.
1835 + * @1 Hotplug event structure
1836 + * @2 Link target, with expandable keys
1837 + * @3 Link name, with expandable keys
1839 + * Returns: return value of symlink()
1841 static int make_symlink(struct hotplug2_event_t *event, char *target, char *linkname) {
1844 @@ -250,11 +352,50 @@
1848 -static int chown_chgrp(int action, char *file, char *param) {
1850 + * Chmod a given file.
1852 + * @1 Hotplug event structure
1853 + * @2 File name, with expandable keys
1854 + * @3 Chmod value, with expandable keys
1856 + * Returns: return value of chmod()
1858 +static int chmod_file(struct hotplug2_event_t *event, char *file, char *value) {
1861 + file = replace_key_by_value(strdup(file), event);
1862 + value = replace_key_by_value(strdup(value), event);
1864 + rv = chmod(file, strtoul(value, 0, 8));
1874 + * Change owner or group of a given file.
1876 + * @1 Hotplug event structure
1877 + * @2 Whether we chown or chgrp
1878 + * @3 Filename, with expandable keys
1879 + * @4 Group or user name, with expandable keys
1881 + * Returns: return value of chown()
1883 +static int chown_chgrp(struct hotplug2_event_t *event, int action, char *file, char *param) {
1889 + file = replace_key_by_value(strdup(file), event);
1890 + param = replace_key_by_value(strdup(param), event);
1896 pwd = getpwnam(param);
1897 @@ -265,11 +406,37 @@
1898 rv = chown(file, -1, grp->gr_gid);
1910 + * Prints all uevent keys.
1912 + * @1 Hotplug event structure
1916 +static void print_debug(struct hotplug2_event_t *event) {
1919 + for (i = 0; i < event->env_vars_c; i++)
1920 + printf("%s=%s\n", event->env_vars[i].key, event->env_vars[i].value);
1923 -static int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
1925 + * Evaluates a condition according to a given hotplug event structure.
1927 + * @1 Hotplug event structure
1928 + * @2 Condition to be evaluated
1930 + * Returns: 1 if match, 0 if no match, EVAL_NOT_AVAILABLE if unable to
1931 + * perform evaluation
1933 +int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
1935 char *event_value = NULL;
1937 @@ -314,6 +481,16 @@
1938 return EVAL_NOT_AVAILABLE;
1942 + * Executes a rule. Contains evaluation of all conditions prior
1945 + * @1 Hotplug event structure
1946 + * @2 The rule to be executed
1948 + * Returns: 0 if success, -1 if the whole event is to be
1949 + * discared, 1 if bail out of this particular rule was required
1951 int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) {
1954 @@ -347,11 +524,11 @@
1955 last_rv = make_dev_from_event(event, rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0));
1958 - last_rv = chmod(rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0));
1959 + last_rv = chmod_file(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]);
1963 - last_rv = chown_chgrp(rule->actions[i].type, rule->actions[i].parameter[0], rule->actions[i].parameter[1]);
1964 + last_rv = chown_chgrp(event, rule->actions[i].type, rule->actions[i].parameter[0], rule->actions[i].parameter[1]);
1967 last_rv = make_symlink(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]);
1968 @@ -365,12 +542,49 @@
1970 last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1);
1973 + last_rv = unlink(rule->actions[i].parameter[0]);
1974 + rmdir_p(rule->actions[i].parameter[0]);
1977 + print_debug(event);
1987 + * Sets the flags of the given rule.
1989 + * @1 Rule structure
1993 +void rule_flags(struct rule_t *rule) {
1996 + for (i = 0; i < rule->actions_c; i++) {
1997 + switch (rule->actions[i].type) {
1998 + case ACT_FLAG_NOTHROTTLE:
1999 + rule->flags |= FLAG_NOTHROTTLE;
2008 + * Checks whether the given character should initiate
2009 + * further parsing.
2011 + * @1 Character to examine
2013 + * Returns: 1 if it should, 0 otherwise
2015 static inline int isinitiator(int c) {
2018 @@ -383,6 +597,16 @@
2023 + * Appends a character to a buffer. Enlarges if necessary.
2025 + * @1 Pointer to the buffer
2026 + * @2 Pointer to buffer size
2027 + * @3 Pointer to last buffer character
2028 + * @4 Appended character
2032 static inline void add_buffer(char **buf, int *blen, int *slen, char c) {
2033 if (*slen + 1 >= *blen) {
2035 @@ -394,6 +618,14 @@
2040 + * Parses a string into a syntactically acceptable value.
2043 + * @2 Pointer to the new position
2045 + * Returns: Newly allocated string.
2047 static char *rules_get_value(char *input, char **nptr) {
2048 int quotes = QUOTES_NONE;
2050 @@ -471,6 +703,16 @@
2055 + * Releases all memory associated with the ruleset. TODO: Make
2056 + * the behavior same for all _free() functions, ie. either
2057 + * release the given pointer itself or keep it, but do it
2058 + * in all functions!
2060 + * @1 The ruleset to be freed
2064 void rules_free(struct rules_t *rules) {
2067 @@ -492,10 +734,53 @@
2071 -struct rules_t *rules_from_config(char *input) {
2072 - int status = STATUS_KEY, terminate;
2074 + * Includes a rule file.
2077 + * @2 The ruleset structure
2079 + * Returns: 0 if success, -1 otherwise
2081 +int rules_include(const char *filename, struct rules_t **return_rules) {
2082 + struct filemap_t filemap;
2083 + struct rules_t *rules;
2085 + if (map_file(filename, &filemap)) {
2086 + ERROR("rules parse","Unable to open/mmap rules file.");
2090 + rules = rules_from_config((char*)(filemap.map), *return_rules);
2091 + if (rules == NULL) {
2092 + ERROR("rules parse","Unable to parse rules file.");
2096 + unmap_file(&filemap);
2102 + * Parses an entire file of rules.
2104 + * @1 The whole file in memory or mmap'd
2106 + * Returns: A newly allocated ruleset.
2108 +struct rules_t *rules_from_config(char *input, struct rules_t *return_rules) {
2109 + #define last_rule return_rules->rules[return_rules->rules_c - 1]
2114 - struct rules_t *return_rules;
2119 + * BIIIG cleanup... Use callbacks for actions and for internal actions.
2123 struct key_rec_t conditions[] = { /*NOTE: We never have parameters for conditions. */
2125 {"!~", 0, COND_NMATCH_RE},
2129 struct key_rec_t actions[] = {
2130 /*one line / one command*/
2131 {"run", 1, ACT_RUN_SHELL},
2133 {"chmod", 2, ACT_CHMOD},
2134 {"chgrp", 2, ACT_CHGRP},
2135 {"setenv", 2, ACT_SETENV},
2136 + {"remove", 1, ACT_REMOVE},
2137 + {"nothrottle", 0, ACT_FLAG_NOTHROTTLE},
2138 + {"printdebug", 0, ACT_DEBUG},
2140 {"symlink", 2, ACT_SYMLINK},
2141 {"softlink", 2, ACT_SYMLINK},
2142 @@ -527,9 +816,19 @@
2146 - return_rules = xmalloc(sizeof(struct rules_t));
2147 - return_rules->rules_c = 1;
2148 - return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c);
2150 + * A little trick for inclusion.
2152 + if (return_rules == NULL) {
2153 + return_rules = xmalloc(sizeof(struct rules_t));
2154 + return_rules->rules_c = 1;
2155 + return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c);
2161 + status = STATUS_KEY;
2163 last_rule.actions = NULL;
2164 last_rule.actions_c = 0;
2165 @@ -549,9 +848,26 @@
2166 /* Skip to next line */
2167 while (*input != '\0' && *input != '\n')
2172 + } else if (buf[0] == '$') {
2176 + * Warning, hack ahead...
2178 + if (!strcmp("include", buf)) {
2179 + buf = rules_get_value(input, &input);
2180 + if (rules_include(buf, &return_rules)) {
2181 + ERROR("rules_include", "Unable to include ruleset '%s'!", buf);
2192 last_rule.conditions_c++;
2193 @@ -684,8 +1000,14 @@
2194 return_rules->rules_c--;
2195 return return_rules;
2197 - rules_free(return_rules);
2198 - free(return_rules);
2200 + * We don't want to cleanup if we're nested.
2203 + rules_free(return_rules);
2204 + free(return_rules);
2210 diff -urN -x .svn hotplug2-0.9/rules.h hotplug2/rules.h
2211 --- hotplug2-0.9/rules.h 2006-09-25 13:42:22.000000000 +0200
2212 +++ hotplug2/rules.h 2007-07-09 02:01:10.962249500 +0200
2214 #define ACT_CHGRP 6 /* chgrp <...> */
2215 #define ACT_CHOWN 7 /* chown <...> */
2216 #define ACT_SYMLINK 8 /* symlink <...> */
2217 -#define ACT_NEXT_EVENT 9 /* next */
2218 +#define ACT_NEXT_EVENT 9 /* next */
2219 #define ACT_NEXT_IF_FAILED 10 /* next_if_failed */
2220 #define ACT_SETENV 11 /* setenv <...> */
2221 +#define ACT_REMOVE 12 /* remove <...> */
2222 +#define ACT_DEBUG 13 /* debug */
2223 +#define ACT_FLAG_NOTHROTTLE 14 /* sets 'nothrottle' flag */
2225 #define EVAL_MATCH 1
2226 #define EVAL_NOT_MATCH 0
2228 #define STATUS_INITIATOR 3 /* ',' for next cond, '{' for block*/
2229 #define STATUS_ACTION 4 /* viz ACT_* and '}' for end of block */
2231 +#define FLAG_UNSET 0
2232 +#define FLAG_ALL 0xffffffff
2233 +#define FLAG_NOTHROTTLE 1 /* We want this rule to ignore max_children limit */
2240 struct action_t *actions;
2243 + unsigned int flags;
2251 +int rule_condition_eval(struct hotplug2_event_t *, struct condition_t *);
2252 int rule_execute(struct hotplug2_event_t *, struct rule_t *);
2253 +void rule_flags(struct rule_t *);
2254 void rules_free(struct rules_t *);
2255 -struct rules_t *rules_from_config(char *);
2256 +struct rules_t *rules_from_config(char *, struct rules_t *);
2258 #endif /* ifndef RULES_H*/
2259 Binary files hotplug2-0.9/.swp and hotplug2/.swp differ
2260 diff -urN -x .svn hotplug2-0.9/TODO hotplug2/TODO
2261 --- hotplug2-0.9/TODO 1970-01-01 01:00:00.000000000 +0100
2262 +++ hotplug2/TODO 2007-06-28 14:51:00.012934184 +0200
2264 + - live rules update (via inotify)